Changeset 55727 in webkit
- Timestamp:
- Mar 9, 2010 9:34:16 AM (14 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r55726 r55727 12 12 * inspector/InspectorController.cpp: 13 13 (WebCore::InspectorController::enableResourceTracking): 14 15 2010-03-09 Pavel Feldman <pfeldman@chromium.org> 16 17 Reviewed by Timothy Hatcher. 18 19 Web Inspector: Refactor Audits panel presentation layer. 20 This change removes unnecessary complexity: 21 - Audit scores were not used 22 - Audit rule parameters are passed as rule constructor arguments 23 - View management aligned with the rest of the front-end 24 - Single TreeOutline is used for category results (no need to create sections for those) 25 - Rules code beautified and simplified where possible 26 - Some UI improvements applied (see attached screenshot) 27 28 https://bugs.webkit.org/show_bug.cgi?id=35860 29 30 * inspector/front-end/AuditCategories.js: 31 (WebInspector.AuditCategories.PagePerformance.prototype.initialize): 32 (WebInspector.AuditCategories.NetworkUtilization.prototype.initialize): 33 * inspector/front-end/AuditResultView.js: 34 (WebInspector.AuditResultView): 35 (WebInspector.AuditCategoryResultPane): 36 (WebInspector.AuditCategoryResultPane.prototype._appendResult): 37 * inspector/front-end/AuditRules.js: 38 (WebInspector.AuditRules.GzipRule.prototype.doRun): 39 (WebInspector.AuditRules.CombineExternalResourcesRule): 40 (WebInspector.AuditRules.CombineExternalResourcesRule.prototype.doRun): 41 (WebInspector.AuditRules.CombineJsResourcesRule): 42 (WebInspector.AuditRules.CombineCssResourcesRule): 43 (WebInspector.AuditRules.MinimizeDnsLookupsRule): 44 (WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype.doRun): 45 (WebInspector.AuditRules.ParallelizeDownloadRule): 46 (WebInspector.AuditRules.ParallelizeDownloadRule.prototype.doRun): 47 (WebInspector.AuditRules.UnusedCssRule): 48 (WebInspector.AuditRules.UnusedCssRule.prototype.doRun.evalCallback): 49 (WebInspector.AuditRules.UnusedCssRule.prototype.doRun.routine): 50 (WebInspector.AuditRules.UnusedCssRule.prototype.doRun): 51 (WebInspector.AuditRules.CacheControlRule): 52 (WebInspector.AuditRules.CacheControlRule.prototype.doRun): 53 (WebInspector.AuditRules.CacheControlRule.prototype.execCheck): 54 (WebInspector.AuditRules.BrowserCacheControlRule): 55 (WebInspector.AuditRules.BrowserCacheControlRule.prototype.handleNonCacheableResources): 56 (WebInspector.AuditRules.BrowserCacheControlRule.prototype.runChecks): 57 (WebInspector.AuditRules.ProxyCacheControlRule): 58 (WebInspector.AuditRules.ProxyCacheControlRule.prototype.runChecks): 59 (WebInspector.AuditRules.ImageDimensionsRule): 60 (WebInspector.AuditRules.ImageDimensionsRule.prototype.doRun): 61 (WebInspector.AuditRules.CssInHeadRule): 62 (WebInspector.AuditRules.CssInHeadRule.prototype.doRun): 63 (WebInspector.AuditRules.CssInHeadRule.prototype.doRun.routine): 64 (WebInspector.AuditRules.StylesScriptsOrderRule): 65 (WebInspector.AuditRules.StylesScriptsOrderRule.prototype.doRun): 66 (WebInspector.AuditRules.StylesScriptsOrderRule.prototype.doRun.routine): 67 (WebInspector.AuditRules.CookieRuleBase): 68 (WebInspector.AuditRules.CookieRuleBase.prototype.doRun.resultCallback): 69 (WebInspector.AuditRules.CookieRuleBase.prototype.doRun): 70 (WebInspector.AuditRules.CookieSizeRule): 71 (WebInspector.AuditRules.CookieSizeRule.prototype.processCookies): 72 (WebInspector.AuditRules.StaticCookielessRule): 73 (WebInspector.AuditRules.StaticCookielessRule.prototype.processCookies): 74 * inspector/front-end/AuditsPanel.js: 75 (WebInspector.AuditsPanel): 76 (WebInspector.AuditsPanel.prototype._executeAudit.ruleResultReadyCallback): 77 (WebInspector.AuditsPanel.prototype._executeAudit): 78 (WebInspector.AuditsPanel.prototype._reloadResources): 79 (WebInspector.AuditsPanel.prototype._didMainResourceLoad): 80 (WebInspector.AuditsPanel.prototype.showResults): 81 (WebInspector.AuditsPanel.prototype.showLauncherView): 82 (WebInspector.AuditsPanel.prototype.get visibleView): 83 (WebInspector.AuditsPanel.prototype.set visibleView): 84 (WebInspector.AuditsPanel.prototype.show): 85 (WebInspector.AuditsPanel.prototype._clearButtonClicked): 86 (WebInspector.AuditCategory.prototype.addRule): 87 (WebInspector.AuditRule): 88 (WebInspector.AuditRule.prototype.set severity): 89 (WebInspector.AuditRule.prototype.run): 90 (WebInspector.AuditRule.prototype.doRun): 91 (WebInspector.AuditCategoryResult): 92 (WebInspector.AuditCategoryResult.prototype.addRuleResult): 93 (WebInspector.AuditRuleResult): 94 (WebInspector.AuditRuleResult.prototype.addChild): 95 (WebInspector.AuditRuleResult.prototype.addURL): 96 (WebInspector.AuditRuleResult.prototype.addURLs): 97 (WebInspector.AuditRuleResult.prototype.addSnippet): 98 * inspector/front-end/Settings.js: 99 * inspector/front-end/audits.css: 100 * inspector/front-end/inspector.js: 101 (WebInspector._createPanels): 102 (WebInspector.documentKeyDown): 14 103 15 104 2010-03-09 Pavel Feldman <pfeldman@chromium.org> -
trunk/WebCore/inspector/front-end/AuditCategories.js
r54591 r55727 38 38 initialize: function() 39 39 { 40 this.addRule(new WebInspector.AuditRules.UnusedCssRule() );41 this.addRule(new WebInspector.AuditRules.CssInHeadRule( {InlineURLScore: 6, InlineStylesheetScore: 21}));42 this.addRule(new WebInspector.AuditRules.StylesScriptsOrderRule( {CSSAfterJSURLScore: 11, InlineBetweenResourcesScore: 21}));40 this.addRule(new WebInspector.AuditRules.UnusedCssRule(), WebInspector.AuditRule.Severity.Warning); 41 this.addRule(new WebInspector.AuditRules.CssInHeadRule(), WebInspector.AuditRule.Severity.Severe); 42 this.addRule(new WebInspector.AuditRules.StylesScriptsOrderRule(), WebInspector.AuditRule.Severity.Severe); 43 43 } 44 44 } … … 55 55 initialize: function() 56 56 { 57 this.addRule(new WebInspector.AuditRules.GzipRule() );58 this.addRule(new WebInspector.AuditRules.ImageDimensionsRule( {ScorePerImageUse: 5}));59 this.addRule(new WebInspector.AuditRules.CookieSizeRule( {MinBytesThreshold: 400, MaxBytesThreshold: 1000}));60 this.addRule(new WebInspector.AuditRules.StaticCookielessRule( {MinResources: 5}));61 this.addRule(new WebInspector.AuditRules.CombineJsResourcesRule( {AllowedPerDomain: 2, ScorePerResource: 11}));62 this.addRule(new WebInspector.AuditRules.CombineCssResourcesRule( {AllowedPerDomain: 2, ScorePerResource: 11}));63 this.addRule(new WebInspector.AuditRules.MinimizeDnsLookupsRule( {HostCountThreshold: 4, ViolationDomainScore: 6}));64 this.addRule(new WebInspector.AuditRules.ParallelizeDownloadRule( {OptimalHostnameCount: 4, MinRequestThreshold: 10, MinBalanceThreshold: 0.5}));65 this.addRule(new WebInspector.AuditRules.BrowserCacheControlRule() );66 this.addRule(new WebInspector.AuditRules.ProxyCacheControlRule() );57 this.addRule(new WebInspector.AuditRules.GzipRule(), WebInspector.AuditRule.Severity.Severe); 58 this.addRule(new WebInspector.AuditRules.ImageDimensionsRule(), WebInspector.AuditRule.Severity.Warning); 59 this.addRule(new WebInspector.AuditRules.CookieSizeRule(400), WebInspector.AuditRule.Severity.Warning); 60 this.addRule(new WebInspector.AuditRules.StaticCookielessRule(5), WebInspector.AuditRule.Severity.Warning); 61 this.addRule(new WebInspector.AuditRules.CombineJsResourcesRule(2), WebInspector.AuditRule.Severity.Severe); 62 this.addRule(new WebInspector.AuditRules.CombineCssResourcesRule(2), WebInspector.AuditRule.Severity.Severe); 63 this.addRule(new WebInspector.AuditRules.MinimizeDnsLookupsRule(4), WebInspector.AuditRule.Severity.Warning); 64 this.addRule(new WebInspector.AuditRules.ParallelizeDownloadRule(4, 10, 0.5), WebInspector.AuditRule.Severity.Warning); 65 this.addRule(new WebInspector.AuditRules.BrowserCacheControlRule(), WebInspector.AuditRule.Severity.Severe); 66 this.addRule(new WebInspector.AuditRules.ProxyCacheControlRule(), WebInspector.AuditRule.Severity.Warning); 67 67 } 68 68 } -
trunk/WebCore/inspector/front-end/AuditResultView.js
r52629 r55727 32 32 { 33 33 WebInspector.View.call(this); 34 this.element.className = "audit-result-view"; 34 35 35 this.element.id = "audit-result-view"; 36 37 function entrySortFunction(a, b) 38 { 39 var result = b.type - a.type; 40 if (!result) 41 result = (a.value || "").localeCompare(b.value || ""); 42 return result; 43 } 44 45 for (var i = 0; i < categoryResults.length; ++i) { 46 var entries = categoryResults[i].entries; 47 if (entries) { 48 entries.sort(entrySortFunction); 49 this.element.appendChild(new WebInspector.AuditCategoryResultPane(categoryResults[i]).element); 50 } 51 } 36 for (var i = 0; i < categoryResults.length; ++i) 37 this.element.appendChild(new WebInspector.AuditCategoryResultPane(categoryResults[i]).element); 52 38 } 53 39 … … 58 44 { 59 45 WebInspector.SidebarPane.call(this, categoryResult.title); 46 var treeOutlineElement = document.createElement("ol"); 47 this.bodyElement.addStyleClass("audit-result-tree"); 48 this.bodyElement.appendChild(treeOutlineElement); 49 50 this._treeOutline = new TreeOutline(treeOutlineElement); 51 this._treeOutline.expandTreeElementsWhenArrowing = true; 52 for (var i = 0; i < categoryResult.ruleResults.length; ++i) { 53 var ruleResult = categoryResult.ruleResults[i]; 54 var treeElement = this._appendResult(this._treeOutline, ruleResult); 55 treeElement.listItemElement.addStyleClass("audit-result"); 56 57 if (ruleResult.severity) { 58 var severityElement = document.createElement("img"); 59 severityElement.className = "severity-" + ruleResult.severity; 60 treeElement.listItemElement.appendChild(severityElement); 61 } 62 } 60 63 this.expand(); 61 for (var i = 0; i < categoryResult.entries.length; ++i) 62 this.bodyElement.appendChild(new WebInspector.AuditRuleResultPane(categoryResult.entries[i]).element); 64 } 65 66 WebInspector.AuditCategoryResultPane.prototype = { 67 _appendResult: function(parentTreeElement, result) 68 { 69 var title = result.value; 70 if (result.violationCount) 71 title = String.sprintf("%s (%d)", title, result.violationCount); 72 73 var treeElement = new TreeElement(title, null, !!result.children); 74 parentTreeElement.appendChild(treeElement); 75 76 if (result.className) 77 treeElement.listItemElement.addStyleClass(result.className); 78 if (result.children) { 79 for (var i = 0; i < result.children.length; ++i) 80 this._appendResult(treeElement, result.children[i]); 81 } 82 if (result.expanded) { 83 treeElement.listItemElement.removeStyleClass("parent"); 84 treeElement.listItemElement.addStyleClass("parent-expanded"); 85 treeElement.expand(); 86 } 87 return treeElement; 88 } 63 89 } 64 90 65 91 WebInspector.AuditCategoryResultPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; 66 67 68 WebInspector.AuditRuleResultPane = function(ruleResult)69 {70 WebInspector.SidebarPane.call(this, ruleResult.value);71 if (!ruleResult.children)72 return;73 74 this._decorateRuleResult(ruleResult);75 76 for (var i = 0; i < ruleResult.children.length; ++i) {77 var section = new WebInspector.AuditRuleResultChildSection(ruleResult.children[i]);78 this.bodyElement.appendChild(section.element);79 }80 }81 82 WebInspector.AuditRuleResultPane.prototype = {83 _decorateRuleResult: function(ruleResult)84 {85 if (ruleResult.type == WebInspector.AuditRuleResult.Type.NA)86 return;87 88 var scoreElement = document.createElement("img");89 scoreElement.className = "score";90 var className = (ruleResult.type == WebInspector.AuditRuleResult.Type.Violation) ? "red" : "green";91 scoreElement.addStyleClass(className);92 this.element.insertBefore(scoreElement, this.element.firstChild);93 }94 }95 96 WebInspector.AuditRuleResultPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;97 98 99 WebInspector.AuditRuleResultChildSection = function(entry)100 {101 WebInspector.Section.call(this, entry.value);102 var children = entry.children;103 this._hasChildren = !!children && children.length;104 if (!this._hasChildren)105 this.element.addStyleClass("blank-section");106 else {107 this.contentElement = document.createElement("div");108 this.contentElement.addStyleClass("section-content");109 for (var i = 0; i < children.length; ++i) {110 var paraElement = document.createElement("p");111 paraElement.innerHTML = children[i].value;112 this.contentElement.appendChild(paraElement);113 }114 this.contentElement.appendChild(paraElement);115 this.element.appendChild(this.contentElement);116 }117 }118 119 WebInspector.AuditRuleResultChildSection.prototype = {120 121 // title is considered pure HTML122 set title(x)123 {124 if (this._title === x)125 return;126 this._title = x;127 128 this.titleElement.innerHTML = x;129 },130 131 expand: function()132 {133 if (this._hasChildren)134 WebInspector.Section.prototype.expand.call(this);135 }136 }137 138 WebInspector.AuditRuleResultChildSection.prototype.__proto__ = WebInspector.Section.prototype; -
trunk/WebCore/inspector/front-end/AuditRules.js
r55466 r55727 43 43 } 44 44 45 /**46 * @param {Array} array Array of Elements (outerHTML is used) or strings (plain value is used as innerHTML)47 */48 WebInspector.AuditRules.arrayAsUL = function(array, shouldLinkify)49 {50 if (!array.length)51 return "";52 var ulElement = document.createElement("ul");53 for (var i = 0; i < array.length; ++i) {54 var liElement = document.createElement("li");55 if (array[i] instanceof Element)56 liElement.appendChild(array[i]);57 else if (shouldLinkify)58 liElement.appendChild(WebInspector.linkifyURLAsNode(array[i]));59 else60 liElement.innerHTML = array[i];61 ulElement.appendChild(liElement);62 }63 return ulElement.outerHTML;64 }65 66 45 WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, regexp, needFullResources) 67 46 { … … 99 78 doRun: function(resources, result, callback) 100 79 { 101 try { 102 var commonMessage = undefined; 103 var totalSavings = 0; 104 var compressedSize = 0 105 var candidateSize = 0 106 var outputResources = []; 107 for (var i = 0, length = resources.length; i < length; ++i) { 108 var resource = resources[i]; 109 if (this._shouldCompress(resource)) { 110 var size = resource.resourceSize; 111 candidateSize += size; 112 if (this._isCompressed(resource)) { 113 compressedSize += size; 114 continue; 115 } 116 if (!commonMessage) 117 commonMessage = result.appendChild(""); 118 var savings = 2 * size / 3; 119 totalSavings += savings; 120 outputResources.push( 121 String.sprintf("Compressing %s could save ~%s", 122 WebInspector.linkifyURL(resource.url), Number.bytesToString(savings))); 80 var totalSavings = 0; 81 var compressedSize = 0; 82 var candidateSize = 0; 83 var summary = result.addChild("", true); 84 for (var i = 0, length = resources.length; i < length; ++i) { 85 var resource = resources[i]; 86 if (this._shouldCompress(resource)) { 87 var size = resource.resourceSize; 88 candidateSize += size; 89 if (this._isCompressed(resource)) { 90 compressedSize += size; 91 continue; 123 92 } 124 } 125 if (commonMessage) { 126 commonMessage.value = 127 String.sprintf("Compressing the following resources with gzip could reduce their " + 128 "transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); 129 commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); 130 result.score = 100 * compressedSize / candidateSize; 131 result.type = WebInspector.AuditRuleResult.Type.Violation; 132 } 133 } catch(e) { 134 console.log(e); 135 } finally { 136 callback(result); 137 } 93 var savings = 2 * size / 3; 94 totalSavings += savings; 95 summary.addChild(String.sprintf("%s could save ~%s", WebInspector.linkifyURL(resource.url), Number.bytesToString(savings))); 96 result.violationCount++; 97 } 98 } 99 if (!totalSavings) 100 return callback(null); 101 summary.value = String.sprintf("Compressing the following resources with gzip could reduce their transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); 102 callback(result); 138 103 }, 139 104 … … 153 118 154 119 155 WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, parametersObject)156 { 157 WebInspector.AuditRule.call(this, id, name , parametersObject);120 WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, allowedPerDomain) 121 { 122 WebInspector.AuditRule.call(this, id, name); 158 123 this._type = type; 159 124 this._resourceTypeName = resourceTypeName; 125 this._allowedPerDomain = allowedPerDomain; 160 126 } 161 127 … … 163 129 doRun: function(resources, result, callback) 164 130 { 165 try { 166 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); 167 var penalizedResourceCount = 0; 168 // TODO: refactor according to the chosen i18n approach 169 for (var domain in domainToResourcesMap) { 170 var domainResources = domainToResourcesMap[domain]; 171 var extraResourceCount = domainResources.length - this.getValue("AllowedPerDomain"); 172 if (extraResourceCount <= 0) 173 continue; 174 penalizedResourceCount += extraResourceCount - 1; 175 result.appendChild( 176 String.sprintf("There are %d %s files served from %s. Consider combining them into as few files as possible.", 177 domainResources.length, this._resourceTypeName, domain)); 178 } 179 result.score = 100 - (penalizedResourceCount * this.getValue("ScorePerResource")); 180 result.type = WebInspector.AuditRuleResult.Type.Hint; 181 } catch(e) { 182 console.log(e); 183 } finally { 184 callback(result); 185 } 186 } 187 }; 131 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); 132 var penalizedResourceCount = 0; 133 // TODO: refactor according to the chosen i18n approach 134 var summary = result.addChild("", true); 135 for (var domain in domainToResourcesMap) { 136 var domainResources = domainToResourcesMap[domain]; 137 var extraResourceCount = domainResources.length - this._allowedPerDomain; 138 if (extraResourceCount <= 0) 139 continue; 140 penalizedResourceCount += extraResourceCount - 1; 141 summary.addChild(String.sprintf("%d %s resources served from %s.", domainResources.length, this._resourceTypeName, domain)); 142 result.violationCount += domainResources.length; 143 } 144 if (!penalizedResourceCount) 145 return callback(null); 146 147 summary.value = "There are multiple resources served from same domain. Consider combining them into as few files as possible."; 148 callback(result); 149 } 150 } 188 151 189 152 WebInspector.AuditRules.CombineExternalResourcesRule.prototype.__proto__ = WebInspector.AuditRule.prototype; 190 153 191 154 192 WebInspector.AuditRules.CombineJsResourcesRule = function( parametersObject) {193 WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "J S", parametersObject);155 WebInspector.AuditRules.CombineJsResourcesRule = function(allowedPerDomain) { 156 WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "JavaScript", allowedPerDomain); 194 157 } 195 158 … … 197 160 198 161 199 WebInspector.AuditRules.CombineCssResourcesRule = function( parametersObject) {200 WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", parametersObject);162 WebInspector.AuditRules.CombineCssResourcesRule = function(allowedPerDomain) { 163 WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", allowedPerDomain); 201 164 } 202 165 … … 204 167 205 168 206 WebInspector.AuditRules.MinimizeDnsLookupsRule = function(parametersObject) { 207 WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups", parametersObject); 169 WebInspector.AuditRules.MinimizeDnsLookupsRule = function(hostCountThreshold) { 170 WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups"); 171 this._hostCountThreshold = hostCountThreshold; 208 172 } 209 173 … … 211 175 doRun: function(resources, result, callback) 212 176 { 213 try { 214 var violationDomains = []; 215 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); 216 for (var domain in domainToResourcesMap) { 217 if (domainToResourcesMap[domain].length > 1) 218 continue; 219 var match = domain.match(WebInspector.URLRegExp); 220 if (!match) 221 continue; 222 if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) 223 continue; // an IP address 224 violationDomains.push(match[2]); 225 } 226 if (violationDomains.length <= this.getValue("HostCountThreshold")) 227 return; 228 var commonMessage = result.appendChild( 229 "The following domains only serve one resource each. If possible, avoid the extra DNS " + 230 "lookups by serving these resources from existing domains."); 231 commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(violationDomains)); 232 result.score = 100 - violationDomains.length * this.getValue("ViolationDomainScore"); 233 } catch(e) { 234 console.log(e); 235 } finally { 236 callback(result); 237 } 177 var summary = result.addChild(""); 178 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); 179 for (var domain in domainToResourcesMap) { 180 if (domainToResourcesMap[domain].length > 1) 181 continue; 182 var match = domain.match(WebInspector.URLRegExp); 183 if (!match) 184 continue; 185 if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) 186 continue; // an IP address 187 summary.addSnippet(match[2]); 188 result.violationCount++; 189 } 190 if (!summary.children || summary.children.length <= this._hostCountThreshold) 191 return callback(null); 192 193 summary.value = "The following domains only serve one resource each. If possible, avoid the extra DNS lookups by serving these resources from existing domains."; 194 callback(result); 238 195 } 239 196 } … … 242 199 243 200 244 WebInspector.AuditRules.ParallelizeDownloadRule = function(parametersObject) 245 { 246 WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames", parametersObject); 201 WebInspector.AuditRules.ParallelizeDownloadRule = function(optimalHostnameCount, minRequestThreshold, minBalanceThreshold) 202 { 203 WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames"); 204 this._optimalHostnameCount = optimalHostnameCount; 205 this._minRequestThreshold = minRequestThreshold; 206 this._minBalanceThreshold = minBalanceThreshold; 247 207 } 248 208 … … 258 218 } 259 219 260 try { 261 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( 262 resources, 263 [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], 264 WebInspector.URLRegExp, 265 true); 266 267 var hosts = []; 268 for (var url in domainToResourcesMap) 269 hosts.push(url); 270 271 if (!hosts.length) 272 return; // no hosts (local file or something) 273 274 hosts.sort(hostSorter); 275 276 var optimalHostnameCount = this.getValue("OptimalHostnameCount"); 277 if (hosts.length > optimalHostnameCount) 278 hosts.splice(optimalHostnameCount); 279 280 var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; 281 var resourceCountAboveThreshold = busiestHostResourceCount - this.getValue("MinRequestThreshold"); 282 if (resourceCountAboveThreshold <= 0) 283 return; 284 285 var avgResourcesPerHost = 0; 286 for (var i = 0, size = hosts.length; i < size; ++i) 287 avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; 288 289 // Assume optimal parallelization. 290 avgResourcesPerHost /= optimalHostnameCount; 291 292 avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); 293 294 var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; 295 296 var minBalanceThreshold = this.getValue("MinBalanceThreshold"); 297 if (pctAboveAvg < minBalanceThreshold) { 298 result.score = 100; 299 return; 300 } 301 302 result.score = (1 - (pctAboveAvg - minBalanceThreshold)) * 100; 303 result.type = WebInspector.AuditRuleResult.Type.Hint; 304 305 var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; 306 var commonMessage = result.appendChild( 307 String.sprintf("This page makes %d parallelizable requests to %s" + 308 ". Increase download parallelization by distributing the following" + 309 " requests across multiple hostnames.", busiestHostResourceCount, hosts[0])); 310 var outputResources = []; 311 for (var i = 0, size = resourcesOnBusiestHost.length; i < size; ++i) 312 outputResources.push(resourcesOnBusiestHost[i].url); 313 commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); 314 } catch(e) { 315 console.log(e); 316 } finally { 317 callback(result); 318 } 220 var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( 221 resources, 222 [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], 223 WebInspector.URLRegExp, 224 true); 225 226 var hosts = []; 227 for (var url in domainToResourcesMap) 228 hosts.push(url); 229 230 if (!hosts.length) 231 return callback(null); // no hosts (local file or something) 232 233 hosts.sort(hostSorter); 234 235 var optimalHostnameCount = this._optimalHostnameCount; 236 if (hosts.length > optimalHostnameCount) 237 hosts.splice(optimalHostnameCount); 238 239 var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; 240 var resourceCountAboveThreshold = busiestHostResourceCount - this._minRequestThreshold; 241 if (resourceCountAboveThreshold <= 0) 242 return callback(null); 243 244 var avgResourcesPerHost = 0; 245 for (var i = 0, size = hosts.length; i < size; ++i) 246 avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; 247 248 // Assume optimal parallelization. 249 avgResourcesPerHost /= optimalHostnameCount; 250 avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); 251 252 var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; 253 var minBalanceThreshold = this._minBalanceThreshold; 254 if (pctAboveAvg < minBalanceThreshold) 255 return callback(null); 256 257 var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; 258 var entry = result.addChild(String.sprintf("This page makes %d parallelizable requests to %s. Increase download parallelization by distributing the following requests across multiple hostnames.", busiestHostResourceCount, hosts[0]), true); 259 for (var i = 0; i < resourcesOnBusiestHost.length; ++i) 260 entry.addURL(resourcesOnBusiestHost[i].url); 261 262 result.violationCount = resourcesOnBusiestHost.length; 263 callback(result); 319 264 } 320 265 } … … 325 270 // The reported CSS rule size is incorrect (parsed != original in WebKit), 326 271 // so use percentages instead, which gives a better approximation. 327 WebInspector.AuditRules.UnusedCssRule = function( parametersObject)328 { 329 WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS ", parametersObject);272 WebInspector.AuditRules.UnusedCssRule = function() 273 { 274 WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS rules"); 330 275 } 331 276 332 277 WebInspector.AuditRules.UnusedCssRule.prototype = { 333 _getUnusedStylesheetRatioMessage: function(unusedLength, type, location, styleSheetLength)334 {335 var url = type === "href"336 ? WebInspector.linkifyURL(location)337 : String.sprintf("Inline block #%s", location);338 var pctUnused = Math.round(unusedLength / styleSheetLength * 100);339 return String.sprintf("%s: %f%% (estimated) is not used by the current page.", url, pctUnused);340 },341 342 _getUnusedTotalRatioMessage: function(unusedLength, totalLength)343 {344 var pctUnused = Math.round(unusedLength / totalLength * 100);345 return String.sprintf("%d%% of CSS (estimated) is not used by the current page.", pctUnused);346 },347 348 278 doRun: function(resources, result, callback) 349 279 { 350 280 var self = this; 351 function evalCallback(evalResult, isException) { 352 try { 353 if (isException) 354 return; 355 356 var totalLength = 0; 357 var totalUnusedLength = 0; 358 var topMessage; 359 var styleSheetMessage; 360 for (var i = 0; i < evalResult.length; ) { 361 var type = evalResult[i++]; 362 if (type === "totalLength") { 363 totalLength = evalResult[i++]; 364 continue; 365 } 366 367 var styleSheetLength = evalResult[i++]; 368 var location = evalResult[i++]; 369 var unusedRules = evalResult[i++]; 370 styleSheetMessage = undefined; 371 if (!topMessage) 372 topMessage = result.appendChild(""); 373 374 var totalUnusedRuleLength = 0; 375 var ruleSelectors = []; 376 for (var j = 0; j < unusedRules.length; ++j) { 377 var rule = unusedRules[j]; 378 totalUnusedRuleLength += parseInt(rule[1]); 379 if (!styleSheetMessage) 380 styleSheetMessage = result.appendChild(""); 381 ruleSelectors.push(rule[0]); 382 } 383 styleSheetMessage.appendChild(WebInspector.AuditRules.arrayAsUL(ruleSelectors)); 384 385 styleSheetMessage.value = self._getUnusedStylesheetRatioMessage(totalUnusedRuleLength, type, location, styleSheetLength); 386 totalUnusedLength += totalUnusedRuleLength; 387 } 388 if (totalUnusedLength) { 389 var totalUnusedPercent = totalUnusedLength / totalLength; 390 topMessage.value = self._getUnusedTotalRatioMessage(totalUnusedLength, totalLength); 391 var pctMultiplier = Math.log(Math.max(200, totalUnusedLength - 800)) / 7 - 0.6; 392 result.score = (1 - totalUnusedPercent * pctMultiplier) * 100; 393 result.type = WebInspector.AuditRuleResult.Type.Hint; 394 } else 395 result.score = 100; 396 } catch(e) { 397 console.log(e); 398 } finally { 399 callback(result); 400 } 281 function evalCallback(routineResult, isException) { 282 if (isException || !routineResult || !routineResult.styleSheets.length) 283 return callback(null); 284 285 var totalUnusedPercent = Math.round(100 * routineResult.unusedSize / routineResult.totalSize); 286 var summary = result.addChild(String.sprintf("%d%% of CSS (estimated) is not used by the current page.", totalUnusedPercent), true); 287 288 for (var i = 0; i < routineResult.styleSheets.length; ++i) { 289 var stylesheet = routineResult.styleSheets[i]; 290 291 var url = stylesheet.type === "href" ? WebInspector.linkifyURL(stylesheet.location) : String.sprintf("Inline block #%s", stylesheet.location); 292 var pctUnused = Math.round(100 * stylesheet.unusedSize / stylesheet.totalSize); 293 var entry = summary.addChild(String.sprintf("%s: %d%% (estimated) is not used by the current page.", url, pctUnused)); 294 295 for (var j = 0; j < stylesheet.unusedRules.length; ++j) 296 entry.addSnippet(stylesheet.unusedRules[j]); 297 298 result.violationCount += stylesheet.unusedRules.length; 299 } 300 301 callback(result); 401 302 } 402 303 … … 405 306 var styleSheets = document.styleSheets; 406 307 if (!styleSheets) 407 return {};408 var styleSheetToUnusedRules = [];308 return false; 309 var routineResult = { styleSheets: [] }; 409 310 var inlineBlockOrdinal = 0; 410 var totalCSSLength = 0; 311 var totalStylesheetSize = 0; 312 var totalUnusedStylesheetSize = 0; 411 313 var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus/; 412 314 for (var i = 0; i < styleSheets.length; ++i) { … … 414 316 if (!styleSheet.cssRules) 415 317 continue; 416 var currentStyleSheetSize = 0; 318 var stylesheetSize = 0; 319 var unusedStylesheetSize = 0; 417 320 var unusedRules = []; 418 321 for (var curRule = 0; curRule < styleSheet.cssRules.length; ++curRule) { 419 322 var rule = styleSheet.cssRules[curRule]; 420 323 var textLength = rule.cssText ? rule.cssText.length : 0; 421 currentStyleSheetSize += textLength; 422 totalCSSLength += textLength; 324 stylesheetSize += textLength; 423 325 if (rule.type !== 1 || rule.selectorText.match(pseudoSelectorRegexp)) 424 326 continue; … … 426 328 if (nodes && nodes.length) 427 329 continue; 428 unusedRules.push([rule.selectorText, textLength]); 330 unusedStylesheetSize += textLength; 331 unusedRules.push(rule.selectorText); 429 332 } 333 totalStylesheetSize += stylesheetSize; 334 totalUnusedStylesheetSize += unusedStylesheetSize; 335 430 336 if (unusedRules.length) { 431 styleSheetToUnusedRules.push(styleSheet.href ? "href" : "inline"); 432 styleSheetToUnusedRules.push(currentStyleSheetSize); 433 styleSheetToUnusedRules.push(styleSheet.href ? styleSheet.href : ++inlineBlockOrdinal); 434 styleSheetToUnusedRules.push(unusedRules); 337 var entry = { type: styleSheet.href ? "href" : "inline", 338 totalSize: stylesheetSize, 339 unusedSize: unusedStylesheetSize, 340 location: styleSheet.href ? styleSheet.href : ++inlineBlockOrdinal, 341 unusedRules: unusedRules }; 342 routineResult.styleSheets.push(entry); 435 343 } 436 344 } 437 styleSheetToUnusedRules.push("totalLength");438 styleSheetToUnusedRules.push(totalCSSLength);439 return styleSheetToUnusedRules;345 routineResult.totalSize = totalStylesheetSize; 346 routineResult.unusedSize = totalUnusedStylesheetSize; 347 return routineResult; 440 348 } 441 349 … … 447 355 448 356 449 WebInspector.AuditRules.CacheControlRule = function(id, name , parametersObject)450 { 451 WebInspector.AuditRule.call(this, id, name , parametersObject);357 WebInspector.AuditRules.CacheControlRule = function(id, name) 358 { 359 WebInspector.AuditRule.call(this, id, name); 452 360 } 453 361 … … 456 364 WebInspector.AuditRules.CacheControlRule.prototype = { 457 365 458 InfoCheck: -1,459 FailCheck: 0,460 WarningCheck: 1,461 SevereCheck: 2,462 463 366 doRun: function(resources, result, callback) 464 367 { 465 try { 466 var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); 467 if (cacheableAndNonCacheableResources[0].length) { 468 result.score = 100; 469 this.runChecks(cacheableAndNonCacheableResources[0], result); 470 } 471 this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); 472 } catch(e) { 473 console.log(e); 474 } finally { 475 callback(result); 476 } 368 var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); 369 if (cacheableAndNonCacheableResources[0].length) 370 this.runChecks(cacheableAndNonCacheableResources[0], result); 371 this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); 372 373 callback(result); 477 374 }, 478 375 … … 496 393 }, 497 394 498 execCheck: function(messageText, resourceCheckFunction, resources, severity, result) 499 { 500 var topMessage; 501 var failingResources = 0; 395 execCheck: function(messageText, resourceCheckFunction, resources, result) 396 { 502 397 var resourceCount = resources.length; 503 var outputResources = [];398 var urls = []; 504 399 for (var i = 0; i < resourceCount; ++i) { 505 if (resourceCheckFunction.call(this, resources[i])) { 506 ++failingResources; 507 if (!topMessage) 508 topMessage = result.appendChild(messageText); 509 outputResources.push(resources[i].url); 510 } 511 } 512 if (topMessage) 513 topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); 514 if (failingResources) { 515 switch (severity) { 516 case this.FailCheck: 517 result.score = 0; 518 result.type = WebInspector.AuditRuleResult.Type.Violation; 519 break; 520 case this.SevereCheck: 521 case this.WarningCheck: 522 result.score -= 50 * severity * failingResources / resourceCount; 523 result.type = WebInspector.AuditRuleResult.Type.Hint; 524 break; 525 } 526 } 527 return topMessage; 400 if (resourceCheckFunction.call(this, resources[i])) 401 urls.push(resources[i].url); 402 } 403 if (urls.length) { 404 var entry = result.addChild(messageText, true); 405 entry.addURLs(urls); 406 result.violationCount += urls.length; 407 } 528 408 }, 529 409 … … 613 493 614 494 615 WebInspector.AuditRules.BrowserCacheControlRule = function( parametersObject)616 { 617 WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching" , parametersObject);495 WebInspector.AuditRules.BrowserCacheControlRule = function() 496 { 497 WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching"); 618 498 } 619 499 … … 622 502 { 623 503 if (resources.length) { 624 var message = result.appendChild( 625 "The following resources are explicitly non-cacheable. Consider making them cacheable if possible:"); 626 var resourceOutput = []; 504 var entry = result.addChild("The following resources are explicitly non-cacheable. Consider making them cacheable if possible:", true); 505 result.violationCount += resources.length; 627 506 for (var i = 0; i < resources.length; ++i) 628 resourceOutput.push(resources[i].url); 629 message.appendChild(WebInspector.AuditRules.arrayAsUL(resourceOutput, true)); 507 entry.addURL(resources[i].url); 630 508 } 631 509 }, … … 633 511 runChecks: function(resources, result, callback) 634 512 { 635 this.execCheck( 636 "The following resources are missing a cache expiration." + 637 " Resources that do not specify an expiration may not be" + 638 " cached by browsers:", 639 this._missingExpirationCheck, resources, this.SevereCheck, result); 640 this.execCheck( 641 "The following resources specify a \"Vary\" header that" + 642 " disables caching in most versions of Internet Explorer:", 643 this._varyCheck, resources, this.SevereCheck, result); 644 this.execCheck( 645 "The following cacheable resources have a short" + 646 " freshness lifetime:", 647 this._oneMonthExpirationCheck, resources, this.WarningCheck, result); 513 this.execCheck("The following resources are missing a cache expiration. Resources that do not specify an expiration may not be cached by browsers:", 514 this._missingExpirationCheck, resources, result); 515 this.execCheck("The following resources specify a \"Vary\" header that disables caching in most versions of Internet Explorer:", 516 this._varyCheck, resources, result); 517 this.execCheck("The following cacheable resources have a short freshness lifetime:", 518 this._oneMonthExpirationCheck, resources, result); 648 519 649 520 // Unable to implement the favicon check due to the WebKit limitations. 650 651 this.execCheck( 652 "To further improve cache hit rate, specify an expiration" + 653 " one year in the future for the following cacheable" + 654 " resources:", 655 this._oneYearExpirationCheck, resources, this.InfoCheck, result); 521 this.execCheck("To further improve cache hit rate, specify an expiration one year in the future for the following cacheable resources:", 522 this._oneYearExpirationCheck, resources, result); 656 523 }, 657 524 … … 692 559 693 560 694 WebInspector.AuditRules.ProxyCacheControlRule = function( parametersObject) {695 WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching" , parametersObject);561 WebInspector.AuditRules.ProxyCacheControlRule = function() { 562 WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching"); 696 563 } 697 564 … … 699 566 runChecks: function(resources, result, callback) 700 567 { 701 this.execCheck( 702 "Resources with a \"?\" in the URL are not cached by most" + 703 " proxy caching servers:", 704 this._questionMarkCheck, resources, this.WarningCheck, result); 705 this.execCheck( 706 "Consider adding a \"Cache-Control: public\" header to the" + 707 " following resources:", 708 this._publicCachingCheck, resources, this.InfoCheck, result); 709 this.execCheck( 710 "The following publicly cacheable resources contain" + 711 " a Set-Cookie header. This security vulnerability" + 712 " can cause cookies to be shared by multiple users.", 713 this._setCookieCacheableCheck, resources, this.FailCheck, result); 568 this.execCheck("Resources with a \"?\" in the URL are not cached by most proxy caching servers:", 569 this._questionMarkCheck, resources, result); 570 this.execCheck("Consider adding a \"Cache-Control: public\" header to the following resources:", 571 this._publicCachingCheck, resources, result); 572 this.execCheck("The following publicly cacheable resources contain a Set-Cookie header. This security vulnerability can cause cookies to be shared by multiple users.", 573 this._setCookieCacheableCheck, resources, result); 714 574 }, 715 575 … … 736 596 737 597 738 WebInspector.AuditRules.ImageDimensionsRule = function( parametersObject)739 { 740 WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions" , parametersObject);598 WebInspector.AuditRules.ImageDimensionsRule = function() 599 { 600 WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions"); 741 601 } 742 602 … … 746 606 function evalCallback(evalResult, isException) 747 607 { 748 try { 749 if (isException) 750 return; 751 if (!evalResult || !evalResult.totalImages) 752 return; 753 result.score = 100; 754 var topMessage = result.appendChild( 755 "A width and height should be specified for all images in order to " + 756 "speed up page display. The following image(s) are missing a width and/or height:"); 757 var map = evalResult.map; 758 var outputResources = []; 759 for (var url in map) { 760 var value = WebInspector.linkifyURL(url); 761 if (map[url] > 1) 762 value += " (" + map[url] + " uses)"; 763 outputResources.push(value); 764 result.score -= this.getValue("ScorePerImageUse") * map[url]; 765 result.type = WebInspector.AuditRuleResult.Type.Hint; 766 } 767 topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); 768 } catch(e) { 769 console.log(e); 770 } finally { 771 callback(result); 772 } 608 if (isException || !evalResult || !evalResult.totalImages) 609 return callback(null); 610 611 var entry = result.addChild("A width and height should be specified for all images in order to speed up page display. The following image(s) are missing a width and/or height:", true); 612 var map = evalResult.map; 613 for (var url in map) { 614 var value = WebInspector.linkifyURL(url); 615 if (map[url] > 1) 616 value += " (" + map[url] + " uses)"; 617 entry.addChild(value); 618 result.violationCount++; 619 } 620 callback(result); 773 621 } 774 622 … … 829 677 830 678 831 WebInspector.AuditRules.CssInHeadRule = function( parametersObject)832 { 833 WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head" , parametersObject);679 WebInspector.AuditRules.CssInHeadRule = function() 680 { 681 WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head"); 834 682 } 835 683 … … 839 687 function evalCallback(evalResult, isException) 840 688 { 841 try { 842 if (isException) 843 return; 844 if (!evalResult) 845 return; 846 result.score = 100; 847 var outputMessages = []; 848 for (var url in evalResult) { 849 var urlViolations = evalResult[url]; 850 var topMessage = result.appendChild( 851 String.sprintf("CSS in the %s document body adversely impacts rendering performance.", 852 WebInspector.linkifyURL(url))); 853 if (urlViolations[0]) { 854 outputMessages.push( 855 String.sprintf("%s style block(s) in the body should be moved to the document head.", urlViolations[0])); 856 result.score -= this.getValue("InlineURLScore") * urlViolations[0]; 857 } 858 for (var i = 0; i < urlViolations[1].length; ++i) { 859 outputMessages.push( 860 String.sprintf("Link node %s should be moved to the document head", WebInspector.linkifyURL(urlViolations[1]))); 861 } 862 result.score -= this.getValue("InlineStylesheetScore") * urlViolations[1]; 863 result.type = WebInspector.AuditRuleResult.Type.Hint; 864 } 865 topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputMessages)); 866 } catch(e) { 867 console.log(e); 868 } finally { 869 callback(result); 870 } 689 if (isException || !evalResult) 690 return callback(null); 691 692 var summary = result.addChild(""); 693 694 var outputMessages = []; 695 for (var url in evalResult) { 696 var urlViolations = evalResult[url]; 697 if (urlViolations[0]) 698 result.addChild(String.sprintf("%s style block(s) in the %s body should be moved to the document head.", urlViolations[0], WebInspector.linkifyURL(url))); 699 for (var i = 0; i < urlViolations[1].length; ++i) 700 result.addChild(String.sprintf("Link node %s should be moved to the document head in %s", WebInspector.linkifyURL(urlViolations[1])), WebInspector.linkifyURL(url)); 701 result.violationCount += urlViolations.length; 702 } 703 summary.value = String.sprintf("CSS in the document body adversely impacts rendering performance."); 704 callback(result); 871 705 } 872 706 … … 892 726 var found = false; 893 727 for (var i = 0; i < views.length; ++i) { 894 var view = views[i]; 895 if (!view.document) 896 continue; 897 898 var inlineStyles = view.document.querySelectorAll("body style"); 899 var inlineStylesheets = view.document.querySelectorAll( 900 "body link[rel~='stylesheet'][href]"); 901 if (!inlineStyles.length && !inlineStylesheets.length) 902 continue; 903 904 found = true; 905 var inlineStylesheetHrefs = []; 906 for (var j = 0; j < inlineStylesheets.length; ++j) 907 inlineStylesheetHrefs.push(inlineStylesheets[j].href); 908 909 urlToViolationsArray[view.location.href] = 910 [inlineStyles.length, inlineStylesheetHrefs]; 728 var view = views[i]; 729 if (!view.document) 730 continue; 731 732 var inlineStyles = view.document.querySelectorAll("body style"); 733 var inlineStylesheets = view.document.querySelectorAll("body link[rel~='stylesheet'][href]"); 734 if (!inlineStyles.length && !inlineStylesheets.length) 735 continue; 736 737 found = true; 738 var inlineStylesheetHrefs = []; 739 for (var j = 0; j < inlineStylesheets.length; ++j) 740 inlineStylesheetHrefs.push(inlineStylesheets[j].href); 741 urlToViolationsArray[view.location.href] = [inlineStyles.length, inlineStylesheetHrefs]; 911 742 } 912 743 return found ? urlToViolationsArray : null; … … 920 751 921 752 922 WebInspector.AuditRules.StylesScriptsOrderRule = function( parametersObject)923 { 924 WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts" , parametersObject);753 WebInspector.AuditRules.StylesScriptsOrderRule = function() 754 { 755 WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts"); 925 756 } 926 757 … … 928 759 doRun: function(resources, result, callback) 929 760 { 930 function evalCallback(evalResult, isException) 931 { 932 try { 933 if (isException) 934 return; 935 if (!evalResult) 936 return; 937 938 result.score = 100; 939 var lateCssUrls = evalResult['late']; 940 if (lateCssUrls) { 941 var lateMessage = result.appendChild( 942 'The following external CSS files were included after ' + 943 'an external JavaScript file in the document head. To ' + 944 'ensure CSS files are downloaded in parallel, always ' + 945 'include external CSS before external JavaScript.'); 946 lateMessage.appendChild(WebInspector.AuditRules.arrayAsUL(lateCssUrls, true)); 947 result.score -= this.getValue("InlineBetweenResourcesScore") * lateCssUrls.length; 948 result.type = WebInspector.AuditRuleResult.Type.Violation; 949 } 950 if (evalResult['cssBeforeInlineCount']) { 951 var count = evalResult['cssBeforeInlineCount']; 952 result.appendChild(count + ' inline script block' + 953 (count > 1 ? 's were' : ' was') + ' found in the head between an ' + 954 'external CSS file and another resource. To allow parallel ' + 955 'downloading, move the inline script before the external CSS ' + 956 'file, or after the next resource.'); 957 result.score -= this.getValue("CSSAfterJSURLScore") * count; 958 result.type = WebInspector.AuditRuleResult.Type.Violation; 959 } 960 } catch(e) { 961 console.log(e); 962 } finally { 963 callback(result); 964 } 761 function evalCallback(resultValue, isException) 762 { 763 if (isException || !resultValue) 764 return callback(null); 765 766 var lateCssUrls = resultValue[0]; 767 var cssBeforeInlineCount = resultValue[1]; 768 769 var entry = result.addChild("The following external CSS files were included after an external JavaScript file in the document head. To ensure CSS files are downloaded in parallel, always include external CSS before external JavaScript.", true); 770 entry.addURLs(lateCssUrls); 771 result.violationCount += lateCssUrls.length; 772 773 if (cssBeforeInlineCount) { 774 result.addChild(String.sprintf(" %d inline script block%s found in the head between an external CSS file and another resource. To allow parallel downloading, move the inline script before the external CSS file, or after the next resource.", cssBeforeInlineCount, cssBeforeInlineCount > 1 ? "s were" : " was")); 775 result.violationCount += cssBeforeInlineCount; 776 } 777 callback(result); 965 778 } 966 779 967 780 function routine() 968 781 { 969 var lateStyles = document.querySelectorAll( 970 "head script[src] ~ link[rel~='stylesheet'][href]"); 971 var stylesBeforeInlineScript = document.querySelectorAll( 972 "head link[rel~='stylesheet'][href] ~ script:not([src])"); 973 974 var resultObject; 975 if (!lateStyles.length && !stylesBeforeInlineScript.length) 976 resultObject = null; 977 else { 978 resultObject = {}; 979 if (lateStyles.length) { 980 lateStyleUrls = []; 981 for (var i = 0; i < lateStyles.length; ++i) 982 lateStyleUrls.push(lateStyles[i].href); 983 resultObject["late"] = lateStyleUrls; 984 } 985 resultObject["cssBeforeInlineCount"] = stylesBeforeInlineScript.length; 986 } 987 return resultObject; 782 var lateStyles = document.querySelectorAll("head script[src] ~ link[rel~='stylesheet'][href]"); 783 var cssBeforeInlineCount = document.querySelectorAll("head link[rel~='stylesheet'][href] ~ script:not([src])").length; 784 if (!lateStyles.length && !cssBeforeInlineCount) 785 return null; 786 787 var lateStyleUrls = []; 788 for (var i = 0; i < lateStyles.length; ++i) 789 lateStyleUrls.push(lateStyles[i].href); 790 return [ lateStyleUrls, cssBeforeInlineCount ]; 988 791 } 989 792 … … 995 798 996 799 997 WebInspector.AuditRules.CookieRuleBase = function(id, name , parametersObject)998 { 999 WebInspector.AuditRule.call(this, id, name , parametersObject);800 WebInspector.AuditRules.CookieRuleBase = function(id, name) 801 { 802 WebInspector.AuditRule.call(this, id, name); 1000 803 } 1001 804 … … 1005 808 var self = this; 1006 809 function resultCallback(receivedCookies, isAdvanced) { 1007 try { 1008 self.processCookies(isAdvanced ? receivedCookies : [], resources, result); 1009 } catch(e) { 1010 console.log(e); 1011 } finally { 1012 callback(result); 1013 } 810 self.processCookies(isAdvanced ? receivedCookies : [], resources, result); 811 callback(result); 1014 812 } 1015 813 WebInspector.Cookies.getCookiesAsync(resultCallback); … … 1040 838 1041 839 1042 WebInspector.AuditRules.CookieSizeRule = function(parametersObject) 1043 { 1044 WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size", parametersObject); 840 WebInspector.AuditRules.CookieSizeRule = function(avgBytesThreshold) 841 { 842 WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size"); 843 this._avgBytesThreshold = avgBytesThreshold; 844 this._maxBytesThreshold = 1000; 1045 845 } 1046 846 … … 1098 898 this.mapResourceCookies(domainToResourcesMap, allCookies, collectorCallback.bind(this)); 1099 899 1100 result.score = 100;1101 900 for (var resourceDomain in cookiesPerResourceDomain) { 1102 901 var cookies = cookiesPerResourceDomain[resourceDomain]; … … 1112 911 sortedCookieSizes.sort(maxSizeSorter); 1113 912 1114 var maxBytesThreshold = this.getValue("MaxBytesThreshold");1115 var minBytesThreshold = this.getValue("MinBytesThreshold");1116 1117 913 for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) { 1118 914 var maxCookieSize = sortedCookieSizes[i].maxCookieSize; 1119 if (maxCookieSize > maxBytesThreshold)915 if (maxCookieSize > this._maxBytesThreshold) 1120 916 hugeCookieDomains.push(sortedCookieSizes[i].domain + ": " + Number.bytesToString(maxCookieSize)); 1121 917 } … … 1126 922 var domain = sortedCookieSizes[i].domain; 1127 923 var avgCookieSize = sortedCookieSizes[i].avgCookieSize; 1128 if (avgCookieSize > minBytesThreshold && avgCookieSize <maxBytesThreshold)924 if (avgCookieSize > this._avgBytesThreshold && avgCookieSize < this._maxBytesThreshold) 1129 925 bigAvgCookieDomains.push(domain + ": " + Number.bytesToString(avgCookieSize)); 1130 926 } 1131 result.a ppendChild("The average cookie size for all requests on this page is " + Number.bytesToString(avgAllCookiesSize));927 result.addChild(String.sprintf("The average cookie size for all requests on this page is %s", Number.bytesToString(avgAllCookiesSize))); 1132 928 1133 929 var message; 1134 930 if (hugeCookieDomains.length) { 1135 result.score = 75; 1136 result.type = WebInspector.AuditRuleResult.Type.Violation; 1137 message = result.appendChild( 1138 String.sprintf("The following domains have a cookie size in excess of %d " + 1139 " bytes. This is harmful because requests with cookies larger than 1KB" + 1140 " typically cannot fit into a single network packet.", maxBytesThreshold)); 1141 message.appendChild(WebInspector.AuditRules.arrayAsUL(hugeCookieDomains)); 931 var entry = result.addChild("The following domains have a cookie size in excess of 1KB. This is harmful because requests with cookies larger than 1KB typically cannot fit into a single network packet.", true); 932 entry.addURLs(hugeCookieDomains); 933 result.violationCount += hugeCookieDomains.length; 1142 934 } 1143 935 1144 936 if (bigAvgCookieDomains.length) { 1145 this.score -= Math.max(0, avgAllCookiesSize - minBytesThreshold) / 1146 (minBytesThreshold - minBytesThreshold) / this.getValue("TotalPoints"); 1147 if (!result.type) 1148 result.type = WebInspector.AuditRuleResult.Type.Hint; 1149 message = result.appendChild( 1150 String.sprintf("The following domains have an average cookie size in excess of %d" + 1151 " bytes. Reducing the size of cookies" + 1152 " for these domains can reduce the time it takes to send requests.", minBytesThreshold)); 1153 message.appendChild(WebInspector.AuditRules.arrayAsUL(bigAvgCookieDomains)); 1154 } 1155 1156 if (!bigAvgCookieDomains.length && !hugeCookieDomains.length) 1157 result.score = WebInspector.AuditCategoryResult.ScoreNA; 937 var entry = result.addChild(String.sprintf("The following domains have an average cookie size in excess of %d bytes. Reducing the size of cookies for these domains can reduce the time it takes to send requests.", this._avgBytesThreshold), true); 938 entry.addURLs(bigAvgCookieDomains.length); 939 result.violationCount += bigAvgCookieDomains.length; 940 } 1158 941 } 1159 942 } … … 1162 945 1163 946 1164 WebInspector.AuditRules.StaticCookielessRule = function(parametersObject) 1165 { 1166 WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain", parametersObject); 947 WebInspector.AuditRules.StaticCookielessRule = function(minResources) 948 { 949 WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain"); 950 this._minResources = minResources; 1167 951 } 1168 952 … … 1176 960 true); 1177 961 var totalStaticResources = 0; 1178 var minResources = this.getValue("MinResources");1179 962 for (var domain in domainToResourcesMap) 1180 963 totalStaticResources += domainToResourcesMap[domain].length; 1181 if (totalStaticResources < minResources)964 if (totalStaticResources < this._minResources) 1182 965 return; 1183 966 var matchingResourceData = {}; … … 1190 973 cookieBytes += matchingResourceData[url] 1191 974 } 1192 if (badUrls.length < minResources)975 if (badUrls.length < this._minResources) 1193 976 return; 1194 977 1195 result.score = 100; 1196 var badPoints = cookieBytes / 75; 1197 var violationPct = Math.max(badUrls.length / totalStaticResources, 0.6); 1198 badPoints *= violationPct; 1199 result.score -= badPoints; 1200 result.score = Math.max(result.score, 0); 1201 result.type = WebInspector.AuditRuleResult.Type.Violation; 1202 result.appendChild(String.sprintf("%s of cookies were sent with the following static resources.", Number.bytesToString(cookieBytes))); 1203 var message = result.appendChild("Serve these static resources from a domain that does not set cookies:"); 1204 message.appendChild(WebInspector.AuditRules.arrayAsUL(badUrls, true)); 978 var entry = result.addChild(String.sprintf("%s of cookies were sent with the following static resources. Serve these static resources from a domain that does not set cookies:", Number.bytesToString(cookieBytes)), true); 979 entry.addURLs(badUrls); 980 result.violationCount = badUrls.length; 1205 981 }, 1206 982 -
trunk/WebCore/inspector/front-end/AuditsPanel.js
r55535 r55727 38 38 this.auditsTreeElement = new WebInspector.SidebarSectionTreeElement("", {}, true); 39 39 this.sidebarTree.appendChild(this.auditsTreeElement); 40 this.auditsTreeElement.listItemElement.addStyleClass("hidden"); 40 41 this.auditsTreeElement.expand(); 41 42 … … 55 56 this.viewsContainerElement.id = "audit-views"; 56 57 this.element.appendChild(this.viewsContainerElement); 58 59 this._launcherView = new WebInspector.AuditLauncherView(this.categoriesById, this.initiateAudit.bind(this)); 57 60 } 58 61 … … 94 97 { 95 98 return this._auditCategoriesById; 96 },97 98 get visibleView()99 {100 return this._visibleView;101 99 }, 102 100 … … 126 124 function ruleResultReadyCallback(categoryResult, ruleResult) 127 125 { 128 if (ruleResult .children)129 categoryResult. entries.push(ruleResult);126 if (ruleResult && ruleResult.children) 127 categoryResult.addRuleResult(ruleResult); 130 128 131 129 --rulesRemaining; … … 193 191 this._updateLauncherViewControls(true); 194 192 } else 195 In jectedScriptAccess.getDefault().evaluate("window.location.reload()", switchCallback);193 InspectorBackend.reloadPage(); 196 194 }, 197 195 … … 200 198 if (this._resourceTrackingCallback) { 201 199 var callback = this._resourceTrackingCallback; 202 this._resourceTrackingCallback = null;200 delete this._resourceTrackingCallback; 203 201 callback(); 204 202 } … … 210 208 categoryResults._resultView = new WebInspector.AuditResultView(categoryResults); 211 209 212 this. showView(categoryResults._resultView);210 this.visibleView = categoryResults._resultView; 213 211 }, 214 212 215 213 showLauncherView: function() 216 214 { 217 if (!this._launcherView) 218 this._launcherView = new WebInspector.AuditLauncherView(this.categoriesById, this.initiateAudit.bind(this)); 219 220 this.showView(this._launcherView); 221 }, 222 223 showView: function(view) 224 { 225 if (view) { 226 if (this._visibleView === view) 227 return; 228 this._closeVisibleView(); 229 this._visibleView = view; 230 } 231 var visibleView = this.visibleView; 232 if (visibleView) 233 visibleView.show(this.viewsContainerElement); 215 this.visibleView = this._launcherView; 216 }, 217 218 get visibleView() 219 { 220 return this._visibleView; 221 }, 222 223 set visibleView(x) 224 { 225 if (this._visibleView === x) 226 return; 227 228 if (this._visibleView) 229 this._visibleView.hide(); 230 231 this._visibleView = x; 232 233 if (x) 234 x.show(this.viewsContainerElement); 234 235 }, 235 236 … … 237 238 { 238 239 WebInspector.Panel.prototype.show.call(this); 239 240 this.showView();241 240 this._updateLauncherViewControls(WebInspector.panels.resources.resourceTrackingEnabled); 242 241 }, … … 265 264 this.auditsItemTreeElement.select(); 266 265 this.auditResultsTreeElement.removeChildren(); 267 },268 269 _closeVisibleView: function()270 {271 if (this.visibleView)272 this.visibleView.hide();273 266 } 274 267 } … … 302 295 }, 303 296 304 addRule: function(rule) 305 { 297 addRule: function(rule, severity) 298 { 299 rule.severity = severity; 306 300 this._rules.push(rule); 307 301 }, … … 325 319 326 320 327 WebInspector.AuditRule = function(id, displayName , parametersObject)321 WebInspector.AuditRule = function(id, displayName) 328 322 { 329 323 this._id = id; 330 324 this._displayName = displayName; 331 this._parametersObject = parametersObject; 325 } 326 327 WebInspector.AuditRule.Severity = { 328 Info: "info", 329 Warning: "warning", 330 Severe: "severe" 332 331 } 333 332 … … 343 342 }, 344 343 344 set severity(severity) 345 { 346 this._severity = severity; 347 }, 348 345 349 run: function(resources, callback) 346 350 { 347 this.doRun(resources, new WebInspector.AuditRuleResult(this.displayName), callback); 351 var result = new WebInspector.AuditRuleResult(this.displayName); 352 result.severity = this._severity; 353 this.doRun(resources, result, callback); 348 354 }, 349 355 … … 351 357 { 352 358 throw new Error("doRun() not implemented"); 353 }, 354 355 getValue: function(key) 356 { 357 if (key in this._parametersObject) 358 return this._parametersObject[key]; 359 else 360 throw new Error(key + " not found in rule parameters"); 361 } 362 } 363 359 } 360 } 364 361 365 362 WebInspector.AuditCategoryResult = function(category) 366 363 { 367 364 this.title = category.displayName; 368 this. entries = [];365 this.ruleResults = []; 369 366 } 370 367 371 368 WebInspector.AuditCategoryResult.prototype = { 372 addEntry: function(value) 373 { 374 var entry = new WebInspector.AuditRuleResult(value); 375 this.entries.push(entry); 376 return entry; 377 } 378 } 379 380 /** 381 * @param {string} value The result message HTML contents. 382 */ 383 WebInspector.AuditRuleResult = function(value) 369 addRuleResult: function(ruleResult) 370 { 371 this.ruleResults.push(ruleResult); 372 } 373 } 374 375 WebInspector.AuditRuleResult = function(value, expanded, className) 384 376 { 385 377 this.value = value; 386 this.type = WebInspector.AuditRuleResult.Type.NA; 387 } 388 389 WebInspector.AuditRuleResult.Type = { 390 // Does not denote a discovered flaw but rather represents an informational message. 391 NA: 0, 392 393 // Denotes a minor impact on the checked metric. 394 Hint: 1, 395 396 // Denotes a major impact on the checked metric. 397 Violation: 2 378 this.className = className; 379 this.expanded = expanded; 380 this.violationCount = 0; 398 381 } 399 382 400 383 WebInspector.AuditRuleResult.prototype = { 401 a ppendChild: function(value)384 addChild: function(value, expanded, className) 402 385 { 403 386 if (!this.children) 404 387 this.children = []; 405 var entry = new WebInspector.AuditRuleResult(value );388 var entry = new WebInspector.AuditRuleResult(value, expanded, className); 406 389 this.children.push(entry); 407 390 return entry; 408 391 }, 409 392 410 set type(x) 411 { 412 this._type = x; 413 }, 414 415 get type() 416 { 417 return this._type; 418 } 419 } 420 393 addURL: function(url) 394 { 395 return this.addChild(WebInspector.linkifyURL(url)); 396 }, 397 398 addURLs: function(urls) 399 { 400 for (var i = 0; i < urls.length; ++i) 401 this.addURL(urls[i]); 402 }, 403 404 addSnippet: function(snippet) 405 { 406 return this.addChild(snippet, false, "source-code"); 407 } 408 } 421 409 422 410 WebInspector.AuditsSidebarTreeElement = function() -
trunk/WebCore/inspector/front-end/Settings.js
r55464 r55727 41 41 showColorNicknames: true, 42 42 debuggerAlwaysEnabled: false, 43 profilerAlwaysEnabled: false 43 profilerAlwaysEnabled: false, 44 auditsPanelEnabled: false 44 45 } 45 46 -
trunk/WebCore/inspector/front-end/audits.css
r54591 r55727 51 51 } 52 52 53 #audit-result-view {54 display: none;55 overflow: auto;56 position: absolute;57 top: 0;58 left: 0;59 right: 0;60 bottom: 0;61 background-color: rgb(245, 245, 245);62 cursor: default;63 overflow: auto;64 }65 66 #audit-result-view.visible {67 display: block;68 }69 70 #audit-result-view > .pane img.score {71 float: left;72 margin-top: 2px;73 position: relative;74 height: 16px;75 width: 16px;76 z-index: 100;77 }78 79 #audit-result-view > .pane img.score.red {80 content: url(Images/errorRedDot.png);81 }82 83 #audit-result-view > .pane img.score.green {84 content: url(Images/successGreenDot.png);85 }86 87 #audit-result-view > .pane.expanded:nth-last-of-type(1) {88 border-bottom: 1px solid rgb(189, 189, 189) !important;89 }90 91 #audit-result-view .pane.expanded:nth-last-of-type(1) {92 border-bottom: 0px transparent none;93 }94 95 #audit-result-view > .pane > .body > .pane > .title {96 padding-left: 16px;97 background-image: none;98 border-bottom: none;99 }100 101 #audit-result-view > .pane > .body > .pane > .body {102 background-color: transparent;103 }104 105 #audit-result-view > .pane > .body > .pane .section {106 margin-left: 16px;107 }108 109 #audit-result-view .section .header {110 border: 0;111 background-image: none;112 background-color: transparent;113 }114 115 #audit-result-view .section .header > .title {116 color: rgb(0, 0, 0);117 }118 119 #audit-result-view .section .section-content {120 width: 100%;121 padding-left: 18px;122 display: none;123 }124 125 #audit-result-view .section.expanded .section-content {126 display: block;127 }128 129 #audit-result-view .section.expanded .section-content > p:nth-of-type(1) {130 margin-top: 0;131 }132 133 #audit-result-view .section.expanded .section-content > p:nth-of-type(1) > *:nth-child(1) {134 margin-top: 0;135 }136 137 #audit-result-view .section .header::before {138 content: url(Images/treeRightTriangleBlack.png);139 }140 141 #audit-result-view .section.expanded .header::before {142 content: url(Images/treeDownTriangleBlack.png);143 }144 145 div.panel.audits .sidebar > ol.sidebar-tree > li:nth-child(1) {146 height: 0px;147 padding-top: 0;148 padding-bottom: 0;149 }150 151 53 .audit-launcher-view { 152 54 z-index: 1000; … … 271 173 -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223))); 272 174 } 175 176 .audit-result-view { 177 overflow: auto; 178 position: absolute; 179 top: 0; 180 left: 0; 181 right: 0; 182 bottom: 0; 183 display: none; 184 } 185 186 .audit-result-view.visible { 187 display: block; 188 } 189 190 .audit-result-view .severity-severe { 191 content: url(Images/errorRedDot.png); 192 } 193 194 .audit-result-view .severity-warning { 195 content: url(Images/warningOrangeDot.png); 196 } 197 198 .audit-result-view .severity-info { 199 content: url(Images/successGreenDot.png); 200 } 201 202 .audit-result-tree li.parent::before { 203 content: url(Images/treeRightTriangleBlack.png); 204 float: left; 205 width: 8px; 206 height: 8px; 207 margin-top: 1px; 208 padding-right: 2px; 209 } 210 211 .audit-result-tree { 212 font-size: 11px; 213 line-height: 14px; 214 } 215 216 .audit-result-tree > ol { 217 position: relative; 218 padding: 2px 6px !important; 219 margin: 0; 220 color: rgb(84, 84, 84); 221 cursor: default; 222 min-width: 100%; 223 } 224 225 .audit-result-tree, .audit-result-tree ol { 226 list-style-type: none; 227 -webkit-padding-start: 12px; 228 margin: 0; 229 } 230 231 .audit-result-tree li { 232 padding: 0 0 0 14px; 233 margin-top: 1px; 234 margin-bottom: 1px; 235 word-wrap: break-word; 236 text-indent: -2px; 237 } 238 239 .audit-result-tree li.parent { 240 text-indent: -12px 241 } 242 243 .audit-result-tree li.parent::before { 244 content: url(Images/treeRightTriangleBlack.png); 245 float: left; 246 width: 8px; 247 height: 8px; 248 margin-top: 0; 249 padding-right: 2px; 250 } 251 252 .audit-result-tree li.parent.expanded::before { 253 content: url(Images/treeDownTriangleBlack.png); 254 } 255 256 .audit-result-tree ol.children { 257 display: none; 258 } 259 260 .audit-result-tree ol.children.expanded { 261 display: block; 262 } 263 264 .audit-result { 265 font-weight: bold; 266 color: black; 267 } 268 269 .audit-result img { 270 float: left; 271 margin-left: -40px; 272 margin-top: -1px; 273 } -
trunk/WebCore/inspector/front-end/inspector.js
r55575 r55727 211 211 this.panels.profiles.registerProfileType(new WebInspector.CPUProfileType()); 212 212 } 213 214 213 if (hiddenPanels.indexOf("storage") === -1 && hiddenPanels.indexOf("databases") === -1) 215 214 this.panels.storage = new WebInspector.StoragePanel(); 216 217 // FIXME: Uncomment when ready. 218 // if (hiddenPanels.indexOf("audits") === -1) 219 // this.panels.audits = new WebInspector.AuditsPanel(); 220 215 if (Preferences.auditsPanelEnabled && hiddenPanels.indexOf("audits") === -1) 216 this.panels.audits = new WebInspector.AuditsPanel(); 221 217 if (hiddenPanels.indexOf("console") === -1) 222 218 this.panels.console = new WebInspector.ConsolePanel(); … … 783 779 break; 784 780 785 case "U+0041": // A key786 if (isMac)787 var shouldShowAuditsPanel = event.metaKey && !event.shiftKey && !event.ctrlKey && event.altKey;788 else789 var shouldShowAuditsPanel = event.ctrlKey && !event.shiftKey && !event.metaKey && event.altKey;790 791 if (shouldShowAuditsPanel) {792 if (!this.panels.audits) {793 this.panels.audits = new WebInspector.AuditsPanel();794 var toolbarElement = document.getElementById("toolbar");795 WebInspector.addPanelToolbarIcon(toolbarElement, this.panels.audits, this.panels.console.toolbarItem);796 }797 this.currentPanel = this.panels.audits;798 }799 800 break;801 781 case "U+0052": // R key 802 782 if ((event.metaKey && isMac) || (event.ctrlKey && !isMac))
Note: See TracChangeset
for help on using the changeset viewer.