Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / audits / AuditsPanel.js
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.PanelWithSidebarTree}
34  */
35 WebInspector.AuditsPanel = function()
36 {
37     WebInspector.PanelWithSidebarTree.call(this, "audits");
38     this.registerRequiredCSS("components/panelEnablerView.css");
39     this.registerRequiredCSS("audits/auditsPanel.css");
40
41     this.auditsTreeElement = new WebInspector.SidebarSectionTreeElement("", {}, true);
42     this.sidebarTree.appendChild(this.auditsTreeElement);
43     this.auditsTreeElement.listItemElement.classList.add("hidden");
44
45     this.auditsItemTreeElement = new WebInspector.AuditsSidebarTreeElement(this);
46     this.auditsTreeElement.appendChild(this.auditsItemTreeElement);
47
48     this.auditResultsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESULTS"), {}, true);
49     this.sidebarTree.appendChild(this.auditResultsTreeElement);
50     this.auditResultsTreeElement.expand();
51
52     this._constructCategories();
53
54     this._auditController = new WebInspector.AuditController(this);
55     this._launcherView = new WebInspector.AuditLauncherView(this._auditController);
56     for (var id in this.categoriesById)
57         this._launcherView.addCategory(this.categoriesById[id]);
58
59     var extensionCategories = WebInspector.extensionServer.auditCategories();
60     for (var i = 0; i < extensionCategories.length; ++i) {
61         var category = extensionCategories[i];
62         this.addCategory(new WebInspector.AuditExtensionCategory(category.extensionOrigin, category.id, category.displayName, category.ruleCount));
63     }
64     WebInspector.extensionServer.addEventListener(WebInspector.ExtensionServer.Events.AuditCategoryAdded, this._extensionAuditCategoryAdded, this);
65 }
66
67 WebInspector.AuditsPanel.prototype = {
68     /**
69      * @return {boolean}
70      */
71     canSearch: function()
72     {
73         return false;
74     },
75
76     /**
77      * @return {!Object.<string, !WebInspector.AuditCategory>}
78      */
79     get categoriesById()
80     {
81         return this._auditCategoriesById;
82     },
83
84     /**
85      * @param {!WebInspector.AuditCategory} category
86      */
87     addCategory: function(category)
88     {
89         this.categoriesById[category.id] = category;
90         this._launcherView.addCategory(category);
91     },
92
93     /**
94      * @param {string} id
95      * @return {!WebInspector.AuditCategory}
96      */
97     getCategory: function(id)
98     {
99         return this.categoriesById[id];
100     },
101
102     _constructCategories: function()
103     {
104         this._auditCategoriesById = {};
105         for (var categoryCtorID in WebInspector.AuditCategories) {
106             var auditCategory = new WebInspector.AuditCategories[categoryCtorID]();
107             auditCategory._id = categoryCtorID;
108             this.categoriesById[categoryCtorID] = auditCategory;
109         }
110     },
111
112     /**
113      * @param {string} mainResourceURL
114      * @param {!Array.<!WebInspector.AuditCategoryResult>} results
115      */
116     auditFinishedCallback: function(mainResourceURL, results)
117     {
118         var children = this.auditResultsTreeElement.children;
119         var ordinal = 1;
120         for (var i = 0; i < children.length; ++i) {
121             if (children[i].mainResourceURL === mainResourceURL)
122                 ordinal++;
123         }
124
125         var resultTreeElement = new WebInspector.AuditResultSidebarTreeElement(this, results, mainResourceURL, ordinal);
126         this.auditResultsTreeElement.appendChild(resultTreeElement);
127         resultTreeElement.revealAndSelect();
128     },
129
130     /**
131      * @param {!Array.<!WebInspector.AuditCategoryResult>} categoryResults
132      */
133     showResults: function(categoryResults)
134     {
135         if (!categoryResults._resultView)
136             categoryResults._resultView = new WebInspector.AuditResultView(categoryResults);
137
138         this.visibleView = categoryResults._resultView;
139     },
140
141     showLauncherView: function()
142     {
143         this.visibleView = this._launcherView;
144     },
145
146     get visibleView()
147     {
148         return this._visibleView;
149     },
150
151     set visibleView(x)
152     {
153         if (this._visibleView === x)
154             return;
155
156         if (this._visibleView)
157             this._visibleView.detach();
158
159         this._visibleView = x;
160
161         if (x)
162             x.show(this.mainElement());
163     },
164
165     wasShown: function()
166     {
167         WebInspector.Panel.prototype.wasShown.call(this);
168         if (!this._visibleView)
169             this.auditsItemTreeElement.select();
170     },
171
172     clearResults: function()
173     {
174         this.auditsItemTreeElement.revealAndSelect();
175         this.auditResultsTreeElement.removeChildren();
176     },
177
178     /**
179      * @param {!WebInspector.Event} event
180      */
181     _extensionAuditCategoryAdded: function(event)
182     {
183         var category = /** @type {!WebInspector.ExtensionAuditCategory} */ (event.data);
184         this.addCategory(new WebInspector.AuditExtensionCategory(category.extensionOrigin, category.id, category.displayName, category.ruleCount));
185     },
186
187     __proto__: WebInspector.PanelWithSidebarTree.prototype
188 }
189
190 /**
191  * @constructor
192  * @implements {WebInspector.AuditCategory}
193  * @param {string} displayName
194  */
195 WebInspector.AuditCategoryImpl = function(displayName)
196 {
197     this._displayName = displayName;
198     this._rules = [];
199 }
200
201 WebInspector.AuditCategoryImpl.prototype = {
202     /**
203      * @override
204      * @return {string}
205      */
206     get id()
207     {
208         // this._id value is injected at construction time.
209         return this._id;
210     },
211
212     /**
213      * @override
214      * @return {string}
215      */
216     get displayName()
217     {
218         return this._displayName;
219     },
220
221     /**
222      * @param {!WebInspector.AuditRule} rule
223      * @param {!WebInspector.AuditRule.Severity} severity
224      */
225     addRule: function(rule, severity)
226     {
227         rule.severity = severity;
228         this._rules.push(rule);
229     },
230
231     /**
232      * @override
233      * @param {!WebInspector.Target} target
234      * @param {!Array.<!WebInspector.NetworkRequest>} requests
235      * @param {function(!WebInspector.AuditRuleResult)} ruleResultCallback
236      * @param {function()} categoryDoneCallback
237      * @param {!WebInspector.Progress} progress
238      */
239     run: function(target, requests, ruleResultCallback, categoryDoneCallback, progress)
240     {
241         this._ensureInitialized();
242         var remainingRulesCount = this._rules.length;
243         progress.setTotalWork(remainingRulesCount);
244         function callbackWrapper(result)
245         {
246             ruleResultCallback(result);
247             progress.worked();
248             if (!--remainingRulesCount)
249                 categoryDoneCallback();
250         }
251         for (var i = 0; i < this._rules.length; ++i)
252             this._rules[i].run(target, requests, callbackWrapper, progress);
253     },
254
255     _ensureInitialized: function()
256     {
257         if (!this._initialized) {
258             if ("initialize" in this)
259                 this.initialize();
260             this._initialized = true;
261         }
262     }
263 }
264
265 /**
266  * @constructor
267  * @param {string} id
268  * @param {string} displayName
269  */
270 WebInspector.AuditRule = function(id, displayName)
271 {
272     this._id = id;
273     this._displayName = displayName;
274 }
275
276 /**
277  * @enum {string}
278  */
279 WebInspector.AuditRule.Severity = {
280     Info: "info",
281     Warning: "warning",
282     Severe: "severe"
283 }
284
285 WebInspector.AuditRule.SeverityOrder = {
286     "info": 3,
287     "warning": 2,
288     "severe": 1
289 }
290
291 WebInspector.AuditRule.prototype = {
292     get id()
293     {
294         return this._id;
295     },
296
297     get displayName()
298     {
299         return this._displayName;
300     },
301
302     /**
303      * @param {!WebInspector.AuditRule.Severity} severity
304      */
305     set severity(severity)
306     {
307         this._severity = severity;
308     },
309
310     /**
311      * @param {!WebInspector.Target} target
312      * @param {!Array.<!WebInspector.NetworkRequest>} requests
313      * @param {function(!WebInspector.AuditRuleResult)} callback
314      * @param {!WebInspector.Progress} progress
315      */
316     run: function(target, requests, callback, progress)
317     {
318         if (progress.isCanceled())
319             return;
320
321         var result = new WebInspector.AuditRuleResult(this.displayName);
322         result.severity = this._severity;
323         this.doRun(target, requests, result, callback, progress);
324     },
325
326     /**
327      * @param {!WebInspector.Target} target
328      * @param {!Array.<!WebInspector.NetworkRequest>} requests
329      * @param {!WebInspector.AuditRuleResult} result
330      * @param {function(!WebInspector.AuditRuleResult)} callback
331      * @param {!WebInspector.Progress} progress
332      */
333     doRun: function(target, requests, result, callback, progress)
334     {
335         throw new Error("doRun() not implemented");
336     }
337 }
338
339 /**
340  * @constructor
341  * @param {!WebInspector.AuditCategory} category
342  */
343 WebInspector.AuditCategoryResult = function(category)
344 {
345     this.title = category.displayName;
346     this.ruleResults = [];
347 }
348
349 WebInspector.AuditCategoryResult.prototype = {
350     /**
351      * @param {!WebInspector.AuditRuleResult} ruleResult
352      */
353     addRuleResult: function(ruleResult)
354     {
355         this.ruleResults.push(ruleResult);
356     }
357 }
358
359 /**
360  * @constructor
361  * @param {(string|boolean|number|!Object)} value
362  * @param {boolean=} expanded
363  * @param {string=} className
364  */
365 WebInspector.AuditRuleResult = function(value, expanded, className)
366 {
367     this.value = value;
368     this.className = className;
369     this.expanded = expanded;
370     this.violationCount = 0;
371     this._formatters = {
372         r: WebInspector.AuditRuleResult.linkifyDisplayName
373     };
374     var standardFormatters = Object.keys(String.standardFormatters);
375     for (var i = 0; i < standardFormatters.length; ++i)
376         this._formatters[standardFormatters[i]] = String.standardFormatters[standardFormatters[i]];
377 }
378
379 /**
380  * @param {string} url
381  * @return {!Element}
382  */
383 WebInspector.AuditRuleResult.linkifyDisplayName = function(url)
384 {
385     return WebInspector.linkifyURLAsNode(url, WebInspector.displayNameForURL(url));
386 }
387
388 /**
389  * @param {string} domain
390  * @return {string}
391  */
392 WebInspector.AuditRuleResult.resourceDomain = function(domain)
393 {
394     return domain || WebInspector.UIString("[empty domain]");
395 }
396
397 WebInspector.AuditRuleResult.prototype = {
398     /**
399      * @param {(string|boolean|number|!Object)} value
400      * @param {boolean=} expanded
401      * @param {string=} className
402      * @return {!WebInspector.AuditRuleResult}
403      */
404     addChild: function(value, expanded, className)
405     {
406         if (!this.children)
407             this.children = [];
408         var entry = new WebInspector.AuditRuleResult(value, expanded, className);
409         this.children.push(entry);
410         return entry;
411     },
412
413     /**
414      * @param {string} url
415      */
416     addURL: function(url)
417     {
418         this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url));
419     },
420
421     /**
422      * @param {!Array.<string>} urls
423      */
424     addURLs: function(urls)
425     {
426         for (var i = 0; i < urls.length; ++i)
427             this.addURL(urls[i]);
428     },
429
430     /**
431      * @param {string} snippet
432      */
433     addSnippet: function(snippet)
434     {
435         this.addChild(snippet, false, "source-code");
436     },
437
438     /**
439      * @param {string} format
440      * @param {...*} vararg
441      * @return {!WebInspector.AuditRuleResult}
442      */
443     addFormatted: function(format, vararg)
444     {
445         var substitutions = Array.prototype.slice.call(arguments, 1);
446         var fragment = createDocumentFragment();
447
448         function append(a, b)
449         {
450             if (!(b instanceof Node))
451                 b = createTextNode(b);
452             a.appendChild(b);
453             return a;
454         }
455
456         var formattedResult = String.format(format, substitutions, this._formatters, fragment, append).formattedResult;
457         if (formattedResult instanceof Node)
458             formattedResult.normalize();
459         return this.addChild(formattedResult);
460     }
461 }
462
463 /**
464  * @constructor
465  * @extends {WebInspector.SidebarTreeElement}
466  * @param {!WebInspector.AuditsPanel} panel
467  */
468 WebInspector.AuditsSidebarTreeElement = function(panel)
469 {
470     this._panel = panel;
471     this.small = false;
472     WebInspector.SidebarTreeElement.call(this, "audits-sidebar-tree-item", WebInspector.UIString("Audits"), "", null, false);
473 }
474
475 WebInspector.AuditsSidebarTreeElement.prototype = {
476     onattach: function()
477     {
478         WebInspector.SidebarTreeElement.prototype.onattach.call(this);
479     },
480
481     /**
482      * @return {boolean}
483      */
484     onselect: function()
485     {
486         this._panel.showLauncherView();
487         return true;
488     },
489
490     get selectable()
491     {
492         return true;
493     },
494
495     refresh: function()
496     {
497         this.refreshTitles();
498     },
499
500     __proto__: WebInspector.SidebarTreeElement.prototype
501 }
502
503 /**
504  * @constructor
505  * @extends {WebInspector.SidebarTreeElement}
506  * @param {!WebInspector.AuditsPanel} panel
507  * @param {!Array.<!WebInspector.AuditCategoryResult>} results
508  * @param {string} mainResourceURL
509  * @param {number} ordinal
510  */
511 WebInspector.AuditResultSidebarTreeElement = function(panel, results, mainResourceURL, ordinal)
512 {
513     this._panel = panel;
514     this.results = results;
515     this.mainResourceURL = mainResourceURL;
516     WebInspector.SidebarTreeElement.call(this, "audit-result-sidebar-tree-item", String.sprintf("%s (%d)", mainResourceURL, ordinal), "", {}, false);
517 }
518
519 WebInspector.AuditResultSidebarTreeElement.prototype = {
520     /**
521      * @return {boolean}
522      */
523     onselect: function()
524     {
525         this._panel.showResults(this.results);
526         return true;
527     },
528
529     get selectable()
530     {
531         return true;
532     },
533
534     __proto__: WebInspector.SidebarTreeElement.prototype
535 }
536
537 WebInspector.AuditsPanel.show = function()
538 {
539     WebInspector.inspectorView.setCurrentPanel(WebInspector.AuditsPanel.instance());
540 }
541
542 /**
543  * @return {!WebInspector.AuditsPanel}
544  */
545 WebInspector.AuditsPanel.instance = function()
546 {
547     if (!WebInspector.AuditsPanel._instanceObject)
548         WebInspector.AuditsPanel._instanceObject = new WebInspector.AuditsPanel();
549     return WebInspector.AuditsPanel._instanceObject;
550 }
551
552 /**
553  * @constructor
554  * @implements {WebInspector.PanelFactory}
555  */
556 WebInspector.AuditsPanelFactory = function()
557 {
558 }
559
560 WebInspector.AuditsPanelFactory.prototype = {
561     /**
562      * @return {!WebInspector.Panel}
563      */
564     createPanel: function()
565     {
566         return WebInspector.AuditsPanel.instance();
567     }
568 }
569
570 // Contributed audit rules should go into this namespace.
571 WebInspector.AuditRules = {};
572
573 /**
574  * Contributed audit categories should go into this namespace.
575  * @type {!Object.<string, function(new:WebInspector.AuditCategory)>}
576  */
577 WebInspector.AuditCategories = {};