2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 WebInspector.AuditsPanel = function()
33 WebInspector.Panel.call(this, "audits");
36 this.auditsTreeElement = new WebInspector.SidebarSectionTreeElement("", {}, true);
37 this.sidebarTree.appendChild(this.auditsTreeElement);
38 this.auditsTreeElement.listItemElement.addStyleClass("hidden");
39 this.auditsTreeElement.expand();
41 this.auditsItemTreeElement = new WebInspector.AuditsSidebarTreeElement();
42 this.auditsTreeElement.appendChild(this.auditsItemTreeElement);
44 this.auditResultsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESULTS"), {}, true);
45 this.sidebarTree.appendChild(this.auditResultsTreeElement);
46 this.auditResultsTreeElement.expand();
48 this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear audit results."), "clear-status-bar-item");
49 this.clearResultsButton.addEventListener("click", this._clearButtonClicked.bind(this), false);
51 this.viewsContainerElement = document.createElement("div");
52 this.viewsContainerElement.id = "audit-views";
53 this.element.appendChild(this.viewsContainerElement);
55 this._constructCategories();
57 this._launcherView = new WebInspector.AuditLauncherView(this.initiateAudit.bind(this));
58 for (id in this.categoriesById)
59 this._launcherView.addCategory(this.categoriesById[id]);
61 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoadEventFired, this);
62 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
65 WebInspector.AuditsPanel.prototype = {
66 get toolbarItemLabel()
68 return WebInspector.UIString("Audits");
73 return [this.clearResultsButton.element];
76 get mainResourceLoadTime()
78 return this._mainResourceLoadTime;
81 _onLoadEventFired: function(event)
83 this._mainResourceLoadTime = event.data;
84 this._didMainResourceLoad();
87 get mainResourceDOMContentTime()
89 return this._mainResourceDOMContentTime;
92 _domContentLoadedEventFired: function(event)
94 this._mainResourceDOMContentTime = event.data;
99 return this._auditCategoriesById;
102 addCategory: function(category)
104 this.categoriesById[category.id] = category;
105 this._launcherView.addCategory(category);
108 getCategory: function(id)
110 return this.categoriesById[id];
113 _constructCategories: function()
115 this._auditCategoriesById = {};
116 for (var categoryCtorID in WebInspector.AuditCategories) {
117 var auditCategory = new WebInspector.AuditCategories[categoryCtorID]();
118 auditCategory._id = categoryCtorID;
119 this.categoriesById[categoryCtorID] = auditCategory;
123 _executeAudit: function(categories, resultCallback)
125 var resources = WebInspector.networkLog.resources;
127 var rulesRemaining = 0;
128 for (var i = 0; i < categories.length; ++i)
129 rulesRemaining += categories[i].ruleCount;
132 var mainResourceURL = WebInspector.mainResource.url;
134 function ruleResultReadyCallback(categoryResult, ruleResult)
136 if (ruleResult && ruleResult.children)
137 categoryResult.addRuleResult(ruleResult);
141 if (!rulesRemaining && resultCallback)
142 resultCallback(mainResourceURL, results);
145 if (!rulesRemaining) {
146 resultCallback(mainResourceURL, results);
150 for (var i = 0; i < categories.length; ++i) {
151 var category = categories[i];
152 var result = new WebInspector.AuditCategoryResult(category);
153 results.push(result);
154 category.run(resources, ruleResultReadyCallback.bind(null, result));
158 _auditFinishedCallback: function(launcherCallback, mainResourceURL, results)
160 var children = this.auditResultsTreeElement.children;
162 for (var i = 0; i < children.length; ++i) {
163 if (children[i].mainResourceURL === mainResourceURL)
167 var resultTreeElement = new WebInspector.AuditResultSidebarTreeElement(results, mainResourceURL, ordinal);
168 this.auditResultsTreeElement.appendChild(resultTreeElement);
169 resultTreeElement.revealAndSelect();
170 if (launcherCallback)
174 initiateAudit: function(categoryIds, runImmediately, launcherCallback)
176 if (!categoryIds || !categoryIds.length)
180 for (var i = 0; i < categoryIds.length; ++i)
181 categories.push(this.categoriesById[categoryIds[i]]);
183 function initiateAuditCallback(categories, launcherCallback)
185 this._executeAudit(categories, this._auditFinishedCallback.bind(this, launcherCallback));
189 initiateAuditCallback.call(this, categories, launcherCallback);
191 this._reloadResources(initiateAuditCallback.bind(this, categories, launcherCallback));
193 WebInspector.userMetrics.AuditsStarted.record();
196 _reloadResources: function(callback)
198 this._pageReloadCallback = callback;
199 PageAgent.reload(false);
202 _didMainResourceLoad: function()
204 if (this._pageReloadCallback) {
205 var callback = this._pageReloadCallback;
206 delete this._pageReloadCallback;
211 showResults: function(categoryResults)
213 if (!categoryResults._resultView)
214 categoryResults._resultView = new WebInspector.AuditResultView(categoryResults);
216 this.visibleView = categoryResults._resultView;
219 showLauncherView: function()
221 this.visibleView = this._launcherView;
226 return this._visibleView;
231 if (this._visibleView === x)
234 if (this._visibleView)
235 this._visibleView.hide();
237 this._visibleView = x;
240 x.show(this.viewsContainerElement);
245 WebInspector.Panel.prototype.attach.call(this);
247 this.auditsItemTreeElement.select();
250 updateMainViewWidth: function(width)
252 this.viewsContainerElement.style.left = width + "px";
255 _clearButtonClicked: function()
257 this.auditsItemTreeElement.revealAndSelect();
258 this.auditResultsTreeElement.removeChildren();
262 WebInspector.AuditsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
266 WebInspector.AuditCategory = function(displayName)
268 this._displayName = displayName;
272 WebInspector.AuditCategory.prototype = {
275 // this._id value is injected at construction time.
281 return this._displayName;
286 this._ensureInitialized();
287 return this._rules.length;
290 addRule: function(rule, severity)
292 rule.severity = severity;
293 this._rules.push(rule);
296 run: function(resources, callback)
298 this._ensureInitialized();
299 for (var i = 0; i < this._rules.length; ++i)
300 this._rules[i].run(resources, callback);
303 _ensureInitialized: function()
305 if (!this._initialized) {
306 if ("initialize" in this)
308 this._initialized = true;
314 WebInspector.AuditRule = function(id, displayName)
317 this._displayName = displayName;
320 WebInspector.AuditRule.Severity = {
326 WebInspector.AuditRule.SeverityOrder = {
332 WebInspector.AuditRule.prototype = {
340 return this._displayName;
343 set severity(severity)
345 this._severity = severity;
348 run: function(resources, callback)
350 var result = new WebInspector.AuditRuleResult(this.displayName);
351 result.severity = this._severity;
352 this.doRun(resources, result, callback);
355 doRun: function(resources, result, callback)
357 throw new Error("doRun() not implemented");
361 WebInspector.AuditCategoryResult = function(category)
363 this.title = category.displayName;
364 this.ruleResults = [];
367 WebInspector.AuditCategoryResult.prototype = {
368 addRuleResult: function(ruleResult)
370 this.ruleResults.push(ruleResult);
374 WebInspector.AuditRuleResult = function(value, expanded, className)
377 this.className = className;
378 this.expanded = expanded;
379 this.violationCount = 0;
382 WebInspector.AuditRuleResult.linkifyDisplayName = function(url)
384 return WebInspector.linkifyURL(url, WebInspector.displayNameForURL(url));
387 WebInspector.AuditRuleResult.resourceDomain = function(domain)
389 return domain || WebInspector.UIString("[empty domain]");
392 WebInspector.AuditRuleResult.prototype = {
393 addChild: function(value, expanded, className)
397 var entry = new WebInspector.AuditRuleResult(value, expanded, className);
398 this.children.push(entry);
402 addURL: function(url)
404 return this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url));
407 addURLs: function(urls)
409 for (var i = 0; i < urls.length; ++i)
410 this.addURL(urls[i]);
413 addSnippet: function(snippet)
415 return this.addChild(snippet, false, "source-code");
419 WebInspector.AuditsSidebarTreeElement = function()
423 WebInspector.SidebarTreeElement.call(this, "audits-sidebar-tree-item", WebInspector.UIString("Audits"), "", null, false);
426 WebInspector.AuditsSidebarTreeElement.prototype = {
429 WebInspector.SidebarTreeElement.prototype.onattach.call(this);
434 WebInspector.panels.audits.showLauncherView();
444 this.refreshTitles();
448 WebInspector.AuditsSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
451 WebInspector.AuditResultSidebarTreeElement = function(results, mainResourceURL, ordinal)
453 this.results = results;
454 this.mainResourceURL = mainResourceURL;
456 WebInspector.SidebarTreeElement.call(this, "audit-result-sidebar-tree-item", String.sprintf("%s (%d)", mainResourceURL, ordinal), "", {}, false);
459 WebInspector.AuditResultSidebarTreeElement.prototype = {
462 WebInspector.panels.audits.showResults(this.results);
471 WebInspector.AuditResultSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
473 // Contributed audit rules should go into this namespace.
474 WebInspector.AuditRules = {};
476 // Contributed audit categories should go into this namespace.
477 WebInspector.AuditCategories = {};