diff options
Diffstat (limited to 'WebCore/inspector/front-end/AuditRules.js')
-rw-r--r-- | WebCore/inspector/front-end/AuditRules.js | 948 |
1 files changed, 382 insertions, 566 deletions
diff --git a/WebCore/inspector/front-end/AuditRules.js b/WebCore/inspector/front-end/AuditRules.js index 210b8a9..d2c5f09 100644 --- a/WebCore/inspector/front-end/AuditRules.js +++ b/WebCore/inspector/front-end/AuditRules.js @@ -42,27 +42,6 @@ WebInspector.AuditRules.CacheableResponseCodes = 304: true // Underlying resource is cacheable } -/** - * @param {Array} array Array of Elements (outerHTML is used) or strings (plain value is used as innerHTML) - */ -WebInspector.AuditRules.arrayAsUL = function(array, shouldLinkify) -{ - if (!array.length) - return ""; - var ulElement = document.createElement("ul"); - for (var i = 0; i < array.length; ++i) { - var liElement = document.createElement("li"); - if (array[i] instanceof Element) - liElement.appendChild(array[i]); - else if (shouldLinkify) - liElement.appendChild(WebInspector.linkifyURLAsNode(array[i])); - else - liElement.innerHTML = array[i]; - ulElement.appendChild(liElement); - } - return ulElement.outerHTML; -} - WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, regexp, needFullResources) { var domainToResourcesMap = {}; @@ -84,9 +63,9 @@ WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, reg return domainToResourcesMap; } -WebInspector.AuditRules.evaluateInTargetWindow = function(func, callback) +WebInspector.AuditRules.evaluateInTargetWindow = function(func, args, callback) { - InjectedScriptAccess.getDefault().evaluateOnSelf(func.toString(), callback); + InjectedScriptAccess.getDefault().evaluateOnSelf(func.toString(), args, callback); } @@ -98,43 +77,29 @@ WebInspector.AuditRules.GzipRule = function() WebInspector.AuditRules.GzipRule.prototype = { doRun: function(resources, result, callback) { - try { - var commonMessage = undefined; - var totalSavings = 0; - var compressedSize = 0 - var candidateSize = 0 - var outputResources = []; - for (var i = 0, length = resources.length; i < length; ++i) { - var resource = resources[i]; - if (this._shouldCompress(resource)) { - var size = resource.contentLength; - candidateSize += size; - if (this._isCompressed(resource)) { - compressedSize += size; - continue; - } - if (!commonMessage) - commonMessage = result.appendChild(""); - var savings = 2 * size / 3; - totalSavings += savings; - outputResources.push( - String.sprintf("Compressing %s could save ~%s", - WebInspector.linkifyURL(resource.url), Number.bytesToString(savings))); + var totalSavings = 0; + var compressedSize = 0; + var candidateSize = 0; + var summary = result.addChild("", true); + for (var i = 0, length = resources.length; i < length; ++i) { + var resource = resources[i]; + if (this._shouldCompress(resource)) { + var size = resource.resourceSize; + candidateSize += size; + if (this._isCompressed(resource)) { + compressedSize += size; + continue; } + var savings = 2 * size / 3; + totalSavings += savings; + summary.addChild(String.sprintf("%s could save ~%s", WebInspector.AuditRuleResult.linkifyDisplayName(resource.url), Number.bytesToString(savings))); + result.violationCount++; } - if (commonMessage) { - commonMessage.value = - String.sprintf("Compressing the following resources with gzip could reduce their " + - "transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); - result.score = 100 * compressedSize / candidateSize; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - } catch(e) { - console.log(e); - } finally { - callback(result); } + if (!totalSavings) + return callback(null); + summary.value = String.sprintf("Compressing the following resources with gzip could reduce their transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); + callback(result); }, _isCompressed: function(resource) @@ -145,105 +110,100 @@ WebInspector.AuditRules.GzipRule.prototype = { _shouldCompress: function(resource) { - return WebInspector.Resource.Type.isTextType(resource.type) && resource.domain && resource.contentLength !== undefined && resource.contentLength > 150; + return WebInspector.Resource.Type.isTextType(resource.type) && resource.domain && resource.resourceSize !== undefined && resource.resourceSize > 150; } } WebInspector.AuditRules.GzipRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, parametersObject) +WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, allowedPerDomain) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); this._type = type; this._resourceTypeName = resourceTypeName; + this._allowedPerDomain = allowedPerDomain; } WebInspector.AuditRules.CombineExternalResourcesRule.prototype = { doRun: function(resources, result, callback) { - try { - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); - var penalizedResourceCount = 0; - // TODO: refactor according to the chosen i18n approach - for (var domain in domainToResourcesMap) { - var domainResources = domainToResourcesMap[domain]; - var extraResourceCount = domainResources.length - this.getValue("AllowedPerDomain"); - if (extraResourceCount <= 0) - continue; - penalizedResourceCount += extraResourceCount - 1; - result.appendChild( - String.sprintf("There are %d %s files served from %s. Consider combining them into as few files as possible.", - domainResources.length, this._resourceTypeName, domain)); - } - result.score = 100 - (penalizedResourceCount * this.getValue("ScorePerResource")); - result.type = WebInspector.AuditRuleResult.Type.Hint; - } catch(e) { - console.log(e); - } finally { - callback(result); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); + var penalizedResourceCount = 0; + // TODO: refactor according to the chosen i18n approach + var summary = result.addChild("", true); + for (var domain in domainToResourcesMap) { + var domainResources = domainToResourcesMap[domain]; + var extraResourceCount = domainResources.length - this._allowedPerDomain; + if (extraResourceCount <= 0) + continue; + penalizedResourceCount += extraResourceCount - 1; + summary.addChild(String.sprintf("%d %s resources served from %s.", domainResources.length, this._resourceTypeName, WebInspector.AuditRuleResult.resourceDomain(domain))); + result.violationCount += domainResources.length; } + if (!penalizedResourceCount) + return callback(null); + + summary.value = "There are multiple resources served from same domain. Consider combining them into as few files as possible."; + callback(result); } -}; +} WebInspector.AuditRules.CombineExternalResourcesRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CombineJsResourcesRule = function(parametersObject) { - WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "JS", parametersObject); +WebInspector.AuditRules.CombineJsResourcesRule = function(allowedPerDomain) { + WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "JavaScript", allowedPerDomain); } WebInspector.AuditRules.CombineJsResourcesRule.prototype.__proto__ = WebInspector.AuditRules.CombineExternalResourcesRule.prototype; -WebInspector.AuditRules.CombineCssResourcesRule = function(parametersObject) { - WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", parametersObject); +WebInspector.AuditRules.CombineCssResourcesRule = function(allowedPerDomain) { + WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", allowedPerDomain); } WebInspector.AuditRules.CombineCssResourcesRule.prototype.__proto__ = WebInspector.AuditRules.CombineExternalResourcesRule.prototype; -WebInspector.AuditRules.MinimizeDnsLookupsRule = function(parametersObject) { - WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups", parametersObject); +WebInspector.AuditRules.MinimizeDnsLookupsRule = function(hostCountThreshold) { + WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups"); + this._hostCountThreshold = hostCountThreshold; } WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype = { doRun: function(resources, result, callback) { - try { - var violationDomains = []; - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); - for (var domain in domainToResourcesMap) { - if (domainToResourcesMap[domain].length > 1) - continue; - var match = domain.match(WebInspector.URLRegExp); - if (!match) - continue; - if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) - continue; // an IP address - violationDomains.push(match[2]); - } - if (violationDomains.length <= this.getValue("HostCountThreshold")) - return; - var commonMessage = result.appendChild( - "The following domains only serve one resource each. If possible, avoid the extra DNS " + - "lookups by serving these resources from existing domains."); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(violationDomains)); - result.score = 100 - violationDomains.length * this.getValue("ViolationDomainScore"); - } catch(e) { - console.log(e); - } finally { - callback(result); + var summary = result.addChild(""); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); + for (var domain in domainToResourcesMap) { + if (domainToResourcesMap[domain].length > 1) + continue; + var match = domain.match(WebInspector.URLRegExp); + if (!match) + continue; + if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) + continue; // an IP address + summary.addSnippet(match[2]); + result.violationCount++; } + if (!summary.children || summary.children.length <= this._hostCountThreshold) + return callback(null); + + summary.value = "The following domains only serve one resource each. If possible, avoid the extra DNS lookups by serving these resources from existing domains."; + callback(result); } } WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.ParallelizeDownloadRule = function(parametersObject) +WebInspector.AuditRules.ParallelizeDownloadRule = function(optimalHostnameCount, minRequestThreshold, minBalanceThreshold) { - WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames", parametersObject); + WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames"); + this._optimalHostnameCount = optimalHostnameCount; + this._minRequestThreshold = minRequestThreshold; + this._minBalanceThreshold = minBalanceThreshold; } @@ -257,65 +217,50 @@ WebInspector.AuditRules.ParallelizeDownloadRule.prototype = { return (aCount < bCount) ? 1 : (aCount == bCount) ? 0 : -1; } - try { - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( - resources, - [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], - WebInspector.URLRegExp, - true); - - var hosts = []; - for (var url in domainToResourcesMap) - hosts.push(url); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( + resources, + [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], + WebInspector.URLRegExp, + true); - if (!hosts.length) - return; // no hosts (local file or something) + var hosts = []; + for (var url in domainToResourcesMap) + hosts.push(url); - hosts.sort(hostSorter); + if (!hosts.length) + return callback(null); // no hosts (local file or something) - var optimalHostnameCount = this.getValue("OptimalHostnameCount"); - if (hosts.length > optimalHostnameCount) - hosts.splice(optimalHostnameCount); + hosts.sort(hostSorter); - var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; - var resourceCountAboveThreshold = busiestHostResourceCount - this.getValue("MinRequestThreshold"); - if (resourceCountAboveThreshold <= 0) - return; + var optimalHostnameCount = this._optimalHostnameCount; + if (hosts.length > optimalHostnameCount) + hosts.splice(optimalHostnameCount); - var avgResourcesPerHost = 0; - for (var i = 0, size = hosts.length; i < size; ++i) - avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; + var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; + var resourceCountAboveThreshold = busiestHostResourceCount - this._minRequestThreshold; + if (resourceCountAboveThreshold <= 0) + return callback(null); - // Assume optimal parallelization. - avgResourcesPerHost /= optimalHostnameCount; + var avgResourcesPerHost = 0; + for (var i = 0, size = hosts.length; i < size; ++i) + avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; - avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); + // Assume optimal parallelization. + avgResourcesPerHost /= optimalHostnameCount; + avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); - var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; + var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; + var minBalanceThreshold = this._minBalanceThreshold; + if (pctAboveAvg < minBalanceThreshold) + return callback(null); - var minBalanceThreshold = this.getValue("MinBalanceThreshold"); - if (pctAboveAvg < minBalanceThreshold) { - result.score = 100; - return; - } + var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; + 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); + for (var i = 0; i < resourcesOnBusiestHost.length; ++i) + entry.addURL(resourcesOnBusiestHost[i].url); - result.score = (1 - (pctAboveAvg - minBalanceThreshold)) * 100; - result.type = WebInspector.AuditRuleResult.Type.Hint; - - var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; - var commonMessage = result.appendChild( - String.sprintf("This page makes %d parallelizable requests to %s" + - ". Increase download parallelization by distributing the following" + - " requests across multiple hostnames.", busiestHostResourceCount, hosts[0])); - var outputResources = []; - for (var i = 0, size = resourcesOnBusiestHost.length; i < size; ++i) - outputResources.push(resourcesOnBusiestHost[i].url); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + result.violationCount = resourcesOnBusiestHost.length; + callback(result); } } @@ -324,156 +269,132 @@ WebInspector.AuditRules.ParallelizeDownloadRule.prototype.__proto__ = WebInspect // The reported CSS rule size is incorrect (parsed != original in WebKit), // so use percentages instead, which gives a better approximation. -WebInspector.AuditRules.UnusedCssRule = function(parametersObject) +WebInspector.AuditRules.UnusedCssRule = function() { - WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS", parametersObject); + WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS rules"); } WebInspector.AuditRules.UnusedCssRule.prototype = { - _getUnusedStylesheetRatioMessage: function(unusedLength, type, location, styleSheetLength) - { - var url = type === "href" - ? WebInspector.linkifyURL(location) - : String.sprintf("Inline block #%s", location); - var pctUnused = Math.round(unusedLength / styleSheetLength * 100); - return String.sprintf("%s: %f%% (estimated) is not used by the current page.", url, pctUnused); - }, - - _getUnusedTotalRatioMessage: function(unusedLength, totalLength) - { - var pctUnused = Math.round(unusedLength / totalLength * 100); - return String.sprintf("%d%% of CSS (estimated) is not used by the current page.", pctUnused); - }, - doRun: function(resources, result, callback) { var self = this; - function evalCallback(evalResult, isException) { - try { - if (isException) - return; - - var totalLength = 0; - var totalUnusedLength = 0; - var topMessage; - var styleSheetMessage; - for (var i = 0; i < evalResult.length; ) { - var type = evalResult[i++]; - if (type === "totalLength") { - totalLength = evalResult[i++]; - continue; - } - - var styleSheetLength = evalResult[i++]; - var location = evalResult[i++]; - var unusedRules = evalResult[i++]; - styleSheetMessage = undefined; - if (!topMessage) - topMessage = result.appendChild(""); - - var totalUnusedRuleLength = 0; - var ruleSelectors = []; - for (var j = 0; j < unusedRules.length; ++j) { - var rule = unusedRules[j]; - totalUnusedRuleLength += parseInt(rule[1]); - if (!styleSheetMessage) - styleSheetMessage = result.appendChild(""); - ruleSelectors.push(rule[0]); - } - styleSheetMessage.appendChild(WebInspector.AuditRules.arrayAsUL(ruleSelectors)); - - styleSheetMessage.value = self._getUnusedStylesheetRatioMessage(totalUnusedRuleLength, type, location, styleSheetLength); - totalUnusedLength += totalUnusedRuleLength; - } - if (totalUnusedLength) { - var totalUnusedPercent = totalUnusedLength / totalLength; - topMessage.value = self._getUnusedTotalRatioMessage(totalUnusedLength, totalLength); - var pctMultiplier = Math.log(Math.max(200, totalUnusedLength - 800)) / 7 - 0.6; - result.score = (1 - totalUnusedPercent * pctMultiplier) * 100; - result.type = WebInspector.AuditRuleResult.Type.Hint; - } else - result.score = 100; - } catch(e) { - console.log(e); - } finally { - callback(result); - } - } + function evalCallback(styleSheets) { + if (!styleSheets.length) + return callback(null); - function routine() - { - var styleSheets = document.styleSheets; - if (!styleSheets) - return {}; - var styleSheetToUnusedRules = []; - var inlineBlockOrdinal = 0; - var totalCSSLength = 0; - var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus/; + var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus|:before|:after/; + var selectors = []; + var testedSelectors = {}; for (var i = 0; i < styleSheets.length; ++i) { var styleSheet = styleSheets[i]; - if (!styleSheet.cssRules) - continue; - var currentStyleSheetSize = 0; - var unusedRules = []; for (var curRule = 0; curRule < styleSheet.cssRules.length; ++curRule) { var rule = styleSheet.cssRules[curRule]; - var textLength = rule.cssText ? rule.cssText.length : 0; - currentStyleSheetSize += textLength; - totalCSSLength += textLength; - if (rule.type !== 1 || rule.selectorText.match(pseudoSelectorRegexp)) + if (rule.selectorText.match(pseudoSelectorRegexp)) continue; - var nodes = document.querySelectorAll(rule.selectorText); - if (nodes && nodes.length) + selectors.push(rule.selectorText); + testedSelectors[rule.selectorText] = 1; + } + } + + function selectorsCallback(callback, styleSheets, testedSelectors, foundSelectors) + { + var inlineBlockOrdinal = 0; + var totalStylesheetSize = 0; + var totalUnusedStylesheetSize = 0; + var summary; + + for (var i = 0; i < styleSheets.length; ++i) { + var styleSheet = styleSheets[i]; + var stylesheetSize = 0; + var unusedStylesheetSize = 0; + var unusedRules = []; + for (var curRule = 0; curRule < styleSheet.cssRules.length; ++curRule) { + var rule = styleSheet.cssRules[curRule]; + var textLength = rule.cssText ? rule.cssText.length : 0; + stylesheetSize += textLength; + if (!testedSelectors[rule.selectorText] || foundSelectors[rule.selectorText]) + continue; + unusedStylesheetSize += textLength; + unusedRules.push(rule.selectorText); + } + totalStylesheetSize += stylesheetSize; + totalUnusedStylesheetSize += unusedStylesheetSize; + + if (!unusedRules.length) continue; - unusedRules.push([rule.selectorText, textLength]); + + var url = styleSheet.href ? WebInspector.AuditRuleResult.linkifyDisplayName(styleSheet.href) : String.sprintf("Inline block #%d", ++inlineBlockOrdinal); + var pctUnused = Math.round(100 * unusedStylesheetSize / stylesheetSize); + if (!summary) + summary = result.addChild("", true); + var entry = summary.addChild(String.sprintf("%s: %d%% (estimated) is not used by the current page.", url, pctUnused)); + + for (var j = 0; j < unusedRules.length; ++j) + entry.addSnippet(unusedRules[j]); + + result.violationCount += unusedRules.length; } - if (unusedRules.length) { - styleSheetToUnusedRules.push(styleSheet.href ? "href" : "inline"); - styleSheetToUnusedRules.push(currentStyleSheetSize); - styleSheetToUnusedRules.push(styleSheet.href ? styleSheet.href : ++inlineBlockOrdinal); - styleSheetToUnusedRules.push(unusedRules); + + if (!totalUnusedStylesheetSize) + return callback(null); + + var totalUnusedPercent = Math.round(100 * totalUnusedStylesheetSize / totalStylesheetSize); + summary.value = String.sprintf("%d%% of CSS (estimated) is not used by the current page.", totalUnusedPercent); + + callback(result); + } + + function routine(selectorArray) + { + var result = {}; + for (var i = 0; i < selectorArray.length; ++i) { + try { + var nodes = document.querySelectorAll(selectorArray[i]); + if (nodes && nodes.length) + result[selectorArray[i]] = true; + } catch(e) { + // ignore and mark as unused + } } + return result; } - styleSheetToUnusedRules.push("totalLength"); - styleSheetToUnusedRules.push(totalCSSLength); - return styleSheetToUnusedRules; + + WebInspector.AuditRules.evaluateInTargetWindow(routine, [selectors], selectorsCallback.bind(null, callback, styleSheets, testedSelectors)); } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback); + function routine() + { + var styleSheets = document.styleSheets; + if (!styleSheets) + return false; + + return routineResult; + } + + InspectorBackend.getAllStyles(WebInspector.Callback.wrap(evalCallback)); } } WebInspector.AuditRules.UnusedCssRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CacheControlRule = function(id, name, parametersObject) +WebInspector.AuditRules.CacheControlRule = function(id, name) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); } WebInspector.AuditRules.CacheControlRule.MillisPerMonth = 1000 * 60 * 60 * 24 * 30; WebInspector.AuditRules.CacheControlRule.prototype = { - InfoCheck: -1, - FailCheck: 0, - WarningCheck: 1, - SevereCheck: 2, - doRun: function(resources, result, callback) { - try { - var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); - if (cacheableAndNonCacheableResources[0].length) { - result.score = 100; - this.runChecks(cacheableAndNonCacheableResources[0], result); - } - this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); + if (cacheableAndNonCacheableResources[0].length) + this.runChecks(cacheableAndNonCacheableResources[0], result); + this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); + + callback(result); }, handleNonCacheableResources: function() @@ -495,36 +416,19 @@ WebInspector.AuditRules.CacheControlRule.prototype = { return processedResources; }, - execCheck: function(messageText, resourceCheckFunction, resources, severity, result) + execCheck: function(messageText, resourceCheckFunction, resources, result) { - var topMessage; - var failingResources = 0; var resourceCount = resources.length; - var outputResources = []; + var urls = []; for (var i = 0; i < resourceCount; ++i) { - if (resourceCheckFunction.call(this, resources[i])) { - ++failingResources; - if (!topMessage) - topMessage = result.appendChild(messageText); - outputResources.push(resources[i].url); - } + if (resourceCheckFunction.call(this, resources[i])) + urls.push(resources[i].url); } - if (topMessage) - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); - if (failingResources) { - switch (severity) { - case this.FailCheck: - result.score = 0; - result.type = WebInspector.AuditRuleResult.Type.Violation; - break; - case this.SevereCheck: - case this.WarningCheck: - result.score -= 50 * severity * failingResources / resourceCount; - result.type = WebInspector.AuditRuleResult.Type.Hint; - break; - } + if (urls.length) { + var entry = result.addChild(messageText, true); + entry.addURLs(urls); + result.violationCount += urls.length; } - return topMessage; }, freshnessLifetimeGreaterThan: function(resource, timeMs) @@ -612,47 +516,34 @@ WebInspector.AuditRules.CacheControlRule.prototype = { WebInspector.AuditRules.CacheControlRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.BrowserCacheControlRule = function(parametersObject) +WebInspector.AuditRules.BrowserCacheControlRule = function() { - WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching", parametersObject); + WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching"); } WebInspector.AuditRules.BrowserCacheControlRule.prototype = { handleNonCacheableResources: function(resources, result) { if (resources.length) { - var message = result.appendChild( - "The following resources are explicitly non-cacheable. Consider making them cacheable if possible:"); - var resourceOutput = []; + var entry = result.addChild("The following resources are explicitly non-cacheable. Consider making them cacheable if possible:", true); + result.violationCount += resources.length; for (var i = 0; i < resources.length; ++i) - resourceOutput.push(resources[i].url); - message.appendChild(WebInspector.AuditRules.arrayAsUL(resourceOutput, true)); + entry.addURL(resources[i].url); } }, runChecks: function(resources, result, callback) { - this.execCheck( - "The following resources are missing a cache expiration." + - " Resources that do not specify an expiration may not be" + - " cached by browsers:", - this._missingExpirationCheck, resources, this.SevereCheck, result); - this.execCheck( - "The following resources specify a \"Vary\" header that" + - " disables caching in most versions of Internet Explorer:", - this._varyCheck, resources, this.SevereCheck, result); - this.execCheck( - "The following cacheable resources have a short" + - " freshness lifetime:", - this._oneMonthExpirationCheck, resources, this.WarningCheck, result); + this.execCheck("The following resources are missing a cache expiration. Resources that do not specify an expiration may not be cached by browsers:", + this._missingExpirationCheck, resources, result); + this.execCheck("The following resources specify a \"Vary\" header that disables caching in most versions of Internet Explorer:", + this._varyCheck, resources, result); + this.execCheck("The following cacheable resources have a short freshness lifetime:", + this._oneMonthExpirationCheck, resources, result); // Unable to implement the favicon check due to the WebKit limitations. - - this.execCheck( - "To further improve cache hit rate, specify an expiration" + - " one year in the future for the following cacheable" + - " resources:", - this._oneYearExpirationCheck, resources, this.InfoCheck, result); + this.execCheck("To further improve cache hit rate, specify an expiration one year in the future for the following cacheable resources:", + this._oneYearExpirationCheck, resources, result); }, _missingExpirationCheck: function(resource) @@ -691,26 +582,19 @@ WebInspector.AuditRules.BrowserCacheControlRule.prototype = { WebInspector.AuditRules.BrowserCacheControlRule.prototype.__proto__ = WebInspector.AuditRules.CacheControlRule.prototype; -WebInspector.AuditRules.ProxyCacheControlRule = function(parametersObject) { - WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching", parametersObject); +WebInspector.AuditRules.ProxyCacheControlRule = function() { + WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching"); } WebInspector.AuditRules.ProxyCacheControlRule.prototype = { runChecks: function(resources, result, callback) { - this.execCheck( - "Resources with a \"?\" in the URL are not cached by most" + - " proxy caching servers:", - this._questionMarkCheck, resources, this.WarningCheck, result); - this.execCheck( - "Consider adding a \"Cache-Control: public\" header to the" + - " following resources:", - this._publicCachingCheck, resources, this.InfoCheck, result); - this.execCheck( - "The following publicly cacheable resources contain" + - " a Set-Cookie header. This security vulnerability" + - " can cause cookies to be shared by multiple users.", - this._setCookieCacheableCheck, resources, this.FailCheck, result); + this.execCheck("Resources with a \"?\" in the URL are not cached by most proxy caching servers:", + this._questionMarkCheck, resources, result); + this.execCheck("Consider adding a \"Cache-Control: public\" header to the following resources:", + this._publicCachingCheck, resources, result); + this.execCheck("The following publicly cacheable resources contain a Set-Cookie header. This security vulnerability can cause cookies to be shared by multiple users.", + this._setCookieCacheableCheck, resources, result); }, _questionMarkCheck: function(resource) @@ -735,102 +619,104 @@ WebInspector.AuditRules.ProxyCacheControlRule.prototype = { WebInspector.AuditRules.ProxyCacheControlRule.prototype.__proto__ = WebInspector.AuditRules.CacheControlRule.prototype; -WebInspector.AuditRules.ImageDimensionsRule = function(parametersObject) +WebInspector.AuditRules.ImageDimensionsRule = function() { - WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions", parametersObject); + WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions"); } WebInspector.AuditRules.ImageDimensionsRule.prototype = { doRun: function(resources, result, callback) { - function evalCallback(evalResult, isException) + function doneCallback(context) { - try { - if (isException) - return; - if (!evalResult || !evalResult.totalImages) - return; - result.score = 100; - var topMessage = result.appendChild( - "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:"); - var map = evalResult.map; - var outputResources = []; - for (var url in map) { - var value = WebInspector.linkifyURL(url); - if (map[url] > 1) - value += " (" + map[url] + " uses)"; - outputResources.push(value); - result.score -= this.getValue("ScorePerImageUse") * map[url]; - result.type = WebInspector.AuditRuleResult.Type.Hint; - } - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); - } catch(e) { - console.log(e); - } finally { - callback(result); + var map = context.urlToNoDimensionCount; + for (var url in map) { + var entry = 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); + var value = WebInspector.AuditRuleResult.linkifyDisplayName(url); + if (map[url] > 1) + value += String.sprintf(" (%d uses)", map[url]); + entry.addChild(value); + result.violationCount++; } + callback(entry ? result : null); } - function routine() + function imageStylesReady(imageId, context, styles) { - var images = document.getElementsByTagName("img"); - const widthRegExp = /width[^:;]*:/gim; - const heightRegExp = /height[^:;]*:/gim; - - function hasDimension(element, cssText, rules, regexp, attributeName) { - if (element.attributes.getNamedItem(attributeName) != null || (cssText && cssText.match(regexp))) - return true; - - if (!rules) - return false; - for (var i = 0; i < rules.length; ++i) { - if (rules.item(i).style.cssText.match(regexp)) - return true; + --context.imagesLeft; + + const node = WebInspector.domAgent.nodeForId(imageId); + var src = node.getAttribute("src"); + for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) { + if (frameOwnerCandidate.documentURL) { + var completeSrc = WebInspector.completeURL(frameOwnerCandidate.documentURL, src); + break; } - return false; } + if (completeSrc) + src = completeSrc; - function hasWidth(element, cssText, rules) { - return hasDimension(element, cssText, rules, widthRegExp, "width"); + const computedStyle = new WebInspector.CSSStyleDeclaration(styles.computedStyle); + if (computedStyle.getPropertyValue("position") === "absolute") { + if (!context.imagesLeft) + doneCallback(context); + return; } - function hasHeight(element, cssText, rules) { - return hasDimension(element, cssText, rules, heightRegExp, "height"); + var widthFound = "width" in styles.styleAttributes; + var heightFound = "height" in styles.styleAttributes; + + for (var i = styles.matchedCSSRules.length - 1; i >= 0 && !(widthFound && heightFound); --i) { + var style = WebInspector.CSSStyleDeclaration.parseRule(styles.matchedCSSRules[i]).style; + if (style.getPropertyValue("width") !== "") + widthFound = true; + if (style.getPropertyValue("height") !== "") + heightFound = true; + } + + if (!widthFound || !heightFound) { + if (src in context.urlToNoDimensionCount) + ++context.urlToNoDimensionCount[src]; + else + context.urlToNoDimensionCount[src] = 1; } - var urlToNoDimensionCount = {}; - var found = false; - for (var i = 0; i < images.length; ++i) { - var image = images[i]; - if (!image.src) - continue; - var position = document.defaultView.getComputedStyle(image).getPropertyValue("position"); - if (position === "absolute") + if (!context.imagesLeft) + doneCallback(context); + } + + function receivedImages(imageIds) + { + if (!imageIds || !imageIds.length) + return callback(null); + var context = {imagesLeft: imageIds.length, urlToNoDimensionCount: {}}; + for (var i = imageIds.length - 1; i >= 0; --i) + InspectorBackend.getStyles(WebInspector.Callback.wrap(imageStylesReady.bind(this, imageIds[i], context)), imageIds[i], true); + } + + function pushImageNodes() + { + const nodeIds = []; + var nodes = document.getElementsByTagName("img"); + for (var i = 0; i < nodes.length; ++i) { + if (!nodes[i].src) continue; - var cssText = (image.style && image.style.cssText) ? image.style.cssText : ""; - var rules = document.defaultView.getMatchedCSSRules(image, "", true); - if (!hasWidth(image, cssText, rules) || !hasHeight(image, cssText, rules)) { - found = true; - if (urlToNoDimensionCount.hasOwnProperty(image.src)) - ++urlToNoDimensionCount[image.src]; - else - urlToNoDimensionCount[image.src] = 1; - } + var nodeId = this.getNodeId(nodes[i]); + nodeIds.push(nodeId); } - return found ? {totalImages: images.length, map: urlToNoDimensionCount} : null; + return nodeIds; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback.bind(this)); + WebInspector.AuditRules.evaluateInTargetWindow(pushImageNodes, null, receivedImages); } } WebInspector.AuditRules.ImageDimensionsRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CssInHeadRule = function(parametersObject) +WebInspector.AuditRules.CssInHeadRule = function() { - WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head", parametersObject); + WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head"); } WebInspector.AuditRules.CssInHeadRule.prototype = { @@ -838,36 +724,24 @@ WebInspector.AuditRules.CssInHeadRule.prototype = { { function evalCallback(evalResult, isException) { - try { - if (isException) - return; - if (!evalResult) - return; - result.score = 100; - var outputMessages = []; - for (var url in evalResult) { - var urlViolations = evalResult[url]; - var topMessage = result.appendChild( - String.sprintf("CSS in the %s document body adversely impacts rendering performance.", - WebInspector.linkifyURL(url))); - if (urlViolations[0]) { - outputMessages.push( - String.sprintf("%s style block(s) in the body should be moved to the document head.", urlViolations[0])); - result.score -= this.getValue("InlineURLScore") * urlViolations[0]; - } - for (var i = 0; i < urlViolations[1].length; ++i) { - outputMessages.push( - String.sprintf("Link node %s should be moved to the document head", WebInspector.linkifyURL(urlViolations[1]))); - } - result.score -= this.getValue("InlineStylesheetScore") * urlViolations[1]; - result.type = WebInspector.AuditRuleResult.Type.Hint; + if (isException || !evalResult) + return callback(null); + + var summary = result.addChild(""); + + var outputMessages = []; + for (var url in evalResult) { + var urlViolations = evalResult[url]; + if (urlViolations[0]) { + result.addChild(String.sprintf("%s style block(s) in the %s body should be moved to the document head.", urlViolations[0], WebInspector.AuditRuleResult.linkifyDisplayName(url))); + result.violationCount += urlViolations[0]; } - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputMessages)); - } catch(e) { - console.log(e); - } finally { - callback(result); + for (var i = 0; i < urlViolations[1].length; ++i) + result.addChild(String.sprintf("Link node %s should be moved to the document head in %s", WebInspector.AuditRuleResult.linkifyDisplayName(urlViolations[1][i]), WebInspector.AuditRuleResult.linkifyDisplayName(url))); + result.violationCount += urlViolations[1].length; } + summary.value = String.sprintf("CSS in the document body adversely impacts rendering performance."); + callback(result); } function routine() @@ -891,112 +765,81 @@ WebInspector.AuditRules.CssInHeadRule.prototype = { var urlToViolationsArray = {}; var found = false; for (var i = 0; i < views.length; ++i) { - var view = views[i]; - if (!view.document) - continue; - - var inlineStyles = view.document.querySelectorAll("body style"); - var inlineStylesheets = view.document.querySelectorAll( - "body link[rel~='stylesheet'][href]"); - if (!inlineStyles.length && !inlineStylesheets.length) - continue; - - found = true; - var inlineStylesheetHrefs = []; - for (var j = 0; j < inlineStylesheets.length; ++j) - inlineStylesheetHrefs.push(inlineStylesheets[j].href); - - urlToViolationsArray[view.location.href] = - [inlineStyles.length, inlineStylesheetHrefs]; + var view = views[i]; + if (!view.document) + continue; + + var inlineStyles = view.document.querySelectorAll("body style"); + var inlineStylesheets = view.document.querySelectorAll("body link[rel~='stylesheet'][href]"); + if (!inlineStyles.length && !inlineStylesheets.length) + continue; + + found = true; + var inlineStylesheetHrefs = []; + for (var j = 0; j < inlineStylesheets.length; ++j) + inlineStylesheetHrefs.push(inlineStylesheets[j].href); + urlToViolationsArray[view.location.href] = [inlineStyles.length, inlineStylesheetHrefs]; } return found ? urlToViolationsArray : null; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback); + WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback); } } WebInspector.AuditRules.CssInHeadRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.StylesScriptsOrderRule = function(parametersObject) +WebInspector.AuditRules.StylesScriptsOrderRule = function() { - WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts", parametersObject); + WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts"); } WebInspector.AuditRules.StylesScriptsOrderRule.prototype = { doRun: function(resources, result, callback) { - function evalCallback(evalResult, isException) + function evalCallback(resultValue, isException) { - try { - if (isException) - return; - if (!evalResult) - return; - - result.score = 100; - var lateCssUrls = evalResult['late']; - if (lateCssUrls) { - var lateMessage = result.appendChild( - '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.'); - lateMessage.appendChild(WebInspector.AuditRules.arrayAsUL(lateCssUrls, true)); - result.score -= this.getValue("InlineBetweenResourcesScore") * lateCssUrls.length; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - if (evalResult['cssBeforeInlineCount']) { - var count = evalResult['cssBeforeInlineCount']; - result.appendChild(count + ' inline script block' + - (count > 1 ? 's were' : ' was') + ' 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.'); - result.score -= this.getValue("CSSAfterJSURLScore") * count; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - } catch(e) { - console.log(e); - } finally { - callback(result); + if (isException || !resultValue) + return callback(null); + + var lateCssUrls = resultValue[0]; + var cssBeforeInlineCount = resultValue[1]; + + 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); + entry.addURLs(lateCssUrls); + result.violationCount += lateCssUrls.length; + + if (cssBeforeInlineCount) { + 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")); + result.violationCount += cssBeforeInlineCount; } + callback(result); } function routine() { - var lateStyles = document.querySelectorAll( - "head script[src] ~ link[rel~='stylesheet'][href]"); - var stylesBeforeInlineScript = document.querySelectorAll( - "head link[rel~='stylesheet'][href] ~ script:not([src])"); - - var resultObject; - if (!lateStyles.length && !stylesBeforeInlineScript.length) - resultObject = null; - else { - resultObject = {}; - if (lateStyles.length) { - lateStyleUrls = []; - for (var i = 0; i < lateStyles.length; ++i) - lateStyleUrls.push(lateStyles[i].href); - resultObject["late"] = lateStyleUrls; - } - resultObject["cssBeforeInlineCount"] = stylesBeforeInlineScript.length; - } - return resultObject; + var lateStyles = document.querySelectorAll("head script[src] ~ link[rel~='stylesheet'][href]"); + var cssBeforeInlineCount = document.querySelectorAll("head link[rel~='stylesheet'][href] ~ script:not([src])").length; + if (!lateStyles.length && !cssBeforeInlineCount) + return null; + + var lateStyleUrls = []; + for (var i = 0; i < lateStyles.length; ++i) + lateStyleUrls.push(lateStyles[i].href); + return [ lateStyleUrls, cssBeforeInlineCount ]; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback.bind(this)); + WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback.bind(this)); } } WebInspector.AuditRules.StylesScriptsOrderRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CookieRuleBase = function(id, name, parametersObject) +WebInspector.AuditRules.CookieRuleBase = function(id, name) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); } WebInspector.AuditRules.CookieRuleBase.prototype = { @@ -1004,13 +847,8 @@ WebInspector.AuditRules.CookieRuleBase.prototype = { { var self = this; function resultCallback(receivedCookies, isAdvanced) { - try { - self.processCookies(isAdvanced ? receivedCookies : [], resources, result); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + self.processCookies(isAdvanced ? receivedCookies : [], resources, result); + callback(result); } WebInspector.Cookies.getCookiesAsync(resultCallback); }, @@ -1039,9 +877,11 @@ WebInspector.AuditRules.CookieRuleBase.prototype = { WebInspector.AuditRules.CookieRuleBase.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CookieSizeRule = function(parametersObject) +WebInspector.AuditRules.CookieSizeRule = function(avgBytesThreshold) { - WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size", parametersObject); + WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size"); + this._avgBytesThreshold = avgBytesThreshold; + this._maxBytesThreshold = 1000; } WebInspector.AuditRules.CookieSizeRule.prototype = { @@ -1097,7 +937,6 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { var matchingResourceData = {}; this.mapResourceCookies(domainToResourcesMap, allCookies, collectorCallback.bind(this)); - result.score = 100; for (var resourceDomain in cookiesPerResourceDomain) { var cookies = cookiesPerResourceDomain[resourceDomain]; sortedCookieSizes.push({ @@ -1111,13 +950,10 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { var hugeCookieDomains = []; sortedCookieSizes.sort(maxSizeSorter); - var maxBytesThreshold = this.getValue("MaxBytesThreshold"); - var minBytesThreshold = this.getValue("MinBytesThreshold"); - for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) { var maxCookieSize = sortedCookieSizes[i].maxCookieSize; - if (maxCookieSize > maxBytesThreshold) - hugeCookieDomains.push(sortedCookieSizes[i].domain + ": " + Number.bytesToString(maxCookieSize)); + if (maxCookieSize > this._maxBytesThreshold) + hugeCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(sortedCookieSizes[i].domain) + ": " + Number.bytesToString(maxCookieSize)); } var bigAvgCookieDomains = []; @@ -1125,45 +961,33 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) { var domain = sortedCookieSizes[i].domain; var avgCookieSize = sortedCookieSizes[i].avgCookieSize; - if (avgCookieSize > minBytesThreshold && avgCookieSize < maxBytesThreshold) - bigAvgCookieDomains.push(domain + ": " + Number.bytesToString(avgCookieSize)); + if (avgCookieSize > this._avgBytesThreshold && avgCookieSize < this._maxBytesThreshold) + bigAvgCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(domain) + ": " + Number.bytesToString(avgCookieSize)); } - result.appendChild("The average cookie size for all requests on this page is " + Number.bytesToString(avgAllCookiesSize)); + result.addChild(String.sprintf("The average cookie size for all requests on this page is %s", Number.bytesToString(avgAllCookiesSize))); var message; if (hugeCookieDomains.length) { - result.score = 75; - result.type = WebInspector.AuditRuleResult.Type.Violation; - message = result.appendChild( - String.sprintf("The following domains have a cookie size in excess of %d " + - " bytes. This is harmful because requests with cookies larger than 1KB" + - " typically cannot fit into a single network packet.", maxBytesThreshold)); - message.appendChild(WebInspector.AuditRules.arrayAsUL(hugeCookieDomains)); + 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); + entry.addURLs(hugeCookieDomains); + result.violationCount += hugeCookieDomains.length; } if (bigAvgCookieDomains.length) { - this.score -= Math.max(0, avgAllCookiesSize - minBytesThreshold) / - (minBytesThreshold - minBytesThreshold) / this.getValue("TotalPoints"); - if (!result.type) - result.type = WebInspector.AuditRuleResult.Type.Hint; - message = result.appendChild( - 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.", minBytesThreshold)); - message.appendChild(WebInspector.AuditRules.arrayAsUL(bigAvgCookieDomains)); + 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); + entry.addURLs(bigAvgCookieDomains); + result.violationCount += bigAvgCookieDomains.length; } - - if (!bigAvgCookieDomains.length && !hugeCookieDomains.length) - result.score = WebInspector.AuditCategoryResult.ScoreNA; } } WebInspector.AuditRules.CookieSizeRule.prototype.__proto__ = WebInspector.AuditRules.CookieRuleBase.prototype; -WebInspector.AuditRules.StaticCookielessRule = function(parametersObject) +WebInspector.AuditRules.StaticCookielessRule = function(minResources) { - WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain", parametersObject); + WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain"); + this._minResources = minResources; } WebInspector.AuditRules.StaticCookielessRule.prototype = { @@ -1175,10 +999,9 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { WebInspector.URLRegExp, true); var totalStaticResources = 0; - var minResources = this.getValue("MinResources"); for (var domain in domainToResourcesMap) totalStaticResources += domainToResourcesMap[domain].length; - if (totalStaticResources < minResources) + if (totalStaticResources < this._minResources) return; var matchingResourceData = {}; this.mapResourceCookies(domainToResourcesMap, allCookies, this._collectorCallback.bind(this, matchingResourceData)); @@ -1189,19 +1012,12 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { badUrls.push(url); cookieBytes += matchingResourceData[url] } - if (badUrls.length < minResources) + if (badUrls.length < this._minResources) return; - result.score = 100; - var badPoints = cookieBytes / 75; - var violationPct = Math.max(badUrls.length / totalStaticResources, 0.6); - badPoints *= violationPct; - result.score -= badPoints; - result.score = Math.max(result.score, 0); - result.type = WebInspector.AuditRuleResult.Type.Violation; - result.appendChild(String.sprintf("%s of cookies were sent with the following static resources.", Number.bytesToString(cookieBytes))); - var message = result.appendChild("Serve these static resources from a domain that does not set cookies:"); - message.appendChild(WebInspector.AuditRules.arrayAsUL(badUrls, true)); + 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); + entry.addURLs(badUrls); + result.violationCount = badUrls.length; }, _collectorCallback: function(matchingResourceData, resource, cookie) |