OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2008 Anthony Ricaud (rik24d@gmail.com) | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
15 * its contributors may be used to endorse or promote products derived | |
16 * from this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 */ | |
29 | |
30 WebInspector.ResourcesPanel = function() | |
31 { | |
32 WebInspector.Panel.call(this); | |
33 | |
34 this.element.addStyleClass("resources"); | |
35 | |
36 this.viewsContainerElement = document.createElement("div"); | |
37 this.viewsContainerElement.id = "resource-views"; | |
38 this.element.appendChild(this.viewsContainerElement); | |
39 | |
40 this.containerElement = document.createElement("div"); | |
41 this.containerElement.id = "resources-container"; | |
42 this.containerElement.addEventListener("scroll", this._updateDividersLabelBa
rPosition.bind(this), false); | |
43 this.element.appendChild(this.containerElement); | |
44 | |
45 this.sidebarElement = document.createElement("div"); | |
46 this.sidebarElement.id = "resources-sidebar"; | |
47 this.sidebarElement.className = "sidebar"; | |
48 this.containerElement.appendChild(this.sidebarElement); | |
49 | |
50 this.sidebarResizeElement = document.createElement("div"); | |
51 this.sidebarResizeElement.className = "sidebar-resizer-vertical"; | |
52 this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDr
agging.bind(this), false); | |
53 this.element.appendChild(this.sidebarResizeElement); | |
54 | |
55 this.containerContentElement = document.createElement("div"); | |
56 this.containerContentElement.id = "resources-container-content"; | |
57 this.containerElement.appendChild(this.containerContentElement); | |
58 | |
59 this.summaryElement = document.createElement("div"); | |
60 this.summaryElement.id = "resources-summary"; | |
61 this.containerContentElement.appendChild(this.summaryElement); | |
62 | |
63 this.resourcesGraphsElement = document.createElement("div"); | |
64 this.resourcesGraphsElement.id = "resources-graphs"; | |
65 this.containerContentElement.appendChild(this.resourcesGraphsElement); | |
66 | |
67 this.dividersElement = document.createElement("div"); | |
68 this.dividersElement.id = "resources-dividers"; | |
69 this.containerContentElement.appendChild(this.dividersElement); | |
70 | |
71 this.dividersLabelBarElement = document.createElement("div"); | |
72 this.dividersLabelBarElement.id = "resources-dividers-label-bar"; | |
73 this.containerContentElement.appendChild(this.dividersLabelBarElement); | |
74 | |
75 this.summaryGraphElement = document.createElement("canvas"); | |
76 this.summaryGraphElement.setAttribute("width", "450"); | |
77 this.summaryGraphElement.setAttribute("height", "38"); | |
78 this.summaryGraphElement.id = "resources-summary-graph"; | |
79 this.summaryElement.appendChild(this.summaryGraphElement); | |
80 | |
81 this.legendElement = document.createElement("div"); | |
82 this.legendElement.id = "resources-graph-legend"; | |
83 this.summaryElement.appendChild(this.legendElement); | |
84 | |
85 this.sidebarTreeElement = document.createElement("ol"); | |
86 this.sidebarTreeElement.className = "sidebar-tree"; | |
87 this.sidebarElement.appendChild(this.sidebarTreeElement); | |
88 | |
89 this.sidebarTree = new TreeOutline(this.sidebarTreeElement); | |
90 | |
91 var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-grap
h-sidebar-item", WebInspector.UIString("Time")); | |
92 timeGraphItem.onselect = this._graphSelected.bind(this); | |
93 | |
94 var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator
(); | |
95 var transferDurationCalculator = new WebInspector.ResourceTransferDurationCa
lculator(); | |
96 | |
97 timeGraphItem.sortingOptions = [ | |
98 { name: WebInspector.UIString("Sort by Start Time"), sortingFunction: We
bInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: t
ransferTimeCalculator }, | |
99 { name: WebInspector.UIString("Sort by Response Time"), sortingFunction:
WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime,
calculator: transferTimeCalculator }, | |
100 { name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebI
nspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: trans
ferTimeCalculator }, | |
101 { name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebI
nspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: tra
nsferDurationCalculator }, | |
102 { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebIn
spector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: trans
ferDurationCalculator }, | |
103 ]; | |
104 | |
105 timeGraphItem.selectedSortingOptionIndex = 1; | |
106 | |
107 var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-grap
h-sidebar-item", WebInspector.UIString("Size")); | |
108 sizeGraphItem.onselect = this._graphSelected.bind(this); | |
109 | |
110 var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator
(); | |
111 sizeGraphItem.sortingOptions = [ | |
112 { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspe
ctor.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSiz
eCalculator }, | |
113 ]; | |
114 | |
115 sizeGraphItem.selectedSortingOptionIndex = 0; | |
116 | |
117 this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspe
ctor.UIString("GRAPHS"), {}, true); | |
118 this.sidebarTree.appendChild(this.graphsTreeElement); | |
119 | |
120 this.graphsTreeElement.appendChild(timeGraphItem); | |
121 this.graphsTreeElement.appendChild(sizeGraphItem); | |
122 this.graphsTreeElement.expand(); | |
123 | |
124 this.resourcesTreeElement = new WebInspector.SidebarSectionTreeElement(WebIn
spector.UIString("RESOURCES"), {}, true); | |
125 this.sidebarTree.appendChild(this.resourcesTreeElement); | |
126 | |
127 this.resourcesTreeElement.expand(); | |
128 | |
129 this.largerResourcesButton = document.createElement("button"); | |
130 this.largerResourcesButton.id = "resources-larger-resources-status-bar-item"
; | |
131 this.largerResourcesButton.className = "status-bar-item toggled-on"; | |
132 this.largerResourcesButton.title = WebInspector.UIString("Use small resource
rows."); | |
133 this.largerResourcesButton.addEventListener("click", this._toggleLargerResou
rces.bind(this), false); | |
134 | |
135 this.sortingSelectElement = document.createElement("select"); | |
136 this.sortingSelectElement.className = "status-bar-item"; | |
137 this.sortingSelectElement.addEventListener("change", this._changeSortingFunc
tion.bind(this), false); | |
138 | |
139 this.reset(); | |
140 | |
141 timeGraphItem.select(); | |
142 } | |
143 | |
144 WebInspector.ResourcesPanel.prototype = { | |
145 toolbarItemClass: "resources", | |
146 | |
147 get toolbarItemLabel() | |
148 { | |
149 return WebInspector.UIString("Resources"); | |
150 }, | |
151 | |
152 get statusBarItems() | |
153 { | |
154 return [this.largerResourcesButton, this.sortingSelectElement]; | |
155 }, | |
156 | |
157 show: function() | |
158 { | |
159 WebInspector.Panel.prototype.show.call(this); | |
160 | |
161 this._updateDividersLabelBarPosition(); | |
162 this._updateSidebarWidth(); | |
163 this.refreshIfNeeded(); | |
164 | |
165 var visibleView = this.visibleView; | |
166 if (visibleView) { | |
167 visibleView.headersVisible = true; | |
168 visibleView.show(this.viewsContainerElement); | |
169 } | |
170 | |
171 // Hide any views that are visible that are not this panel's current vis
ible view. | |
172 // This can happen when a ResourceView is visible in the Scripts panel t
hen switched | |
173 // to the this panel. | |
174 var resourcesLength = this._resources.length; | |
175 for (var i = 0; i < resourcesLength; ++i) { | |
176 var resource = this._resources[i]; | |
177 var view = resource._resourcesView; | |
178 if (!view || view === visibleView) | |
179 continue; | |
180 view.visible = false; | |
181 } | |
182 }, | |
183 | |
184 resize: function() | |
185 { | |
186 this._updateGraphDividersIfNeeded(); | |
187 | |
188 var visibleView = this.visibleView; | |
189 if (visibleView && "resize" in visibleView) | |
190 visibleView.resize(); | |
191 }, | |
192 | |
193 get searchableViews() | |
194 { | |
195 var views = []; | |
196 | |
197 const visibleView = this.visibleView; | |
198 if (visibleView && visibleView.performSearch) | |
199 views.push(visibleView); | |
200 | |
201 var resourcesLength = this._resources.length; | |
202 for (var i = 0; i < resourcesLength; ++i) { | |
203 var resource = this._resources[i]; | |
204 if (!resource._resourcesTreeElement) | |
205 continue; | |
206 var resourceView = this.resourceViewForResource(resource); | |
207 if (!resourceView.performSearch || resourceView === visibleView) | |
208 continue; | |
209 views.push(resourceView); | |
210 } | |
211 | |
212 return views; | |
213 }, | |
214 | |
215 get searchResultsSortFunction() | |
216 { | |
217 const resourceTreeElementSortFunction = this.sortingFunction; | |
218 | |
219 function sortFuction(a, b) | |
220 { | |
221 return resourceTreeElementSortFunction(a.resource._resourcesTreeElem
ent, b.resource._resourcesTreeElement); | |
222 } | |
223 | |
224 return sortFuction; | |
225 }, | |
226 | |
227 searchMatchFound: function(view, matches) | |
228 { | |
229 view.resource._resourcesTreeElement.searchMatches = matches; | |
230 }, | |
231 | |
232 searchCanceled: function(startingNewSearch) | |
233 { | |
234 WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch
); | |
235 | |
236 if (startingNewSearch || !this._resources) | |
237 return; | |
238 | |
239 for (var i = 0; i < this._resources.length; ++i) { | |
240 var resource = this._resources[i]; | |
241 if (resource._resourcesTreeElement) | |
242 resource._resourcesTreeElement.updateErrorsAndWarnings(); | |
243 } | |
244 }, | |
245 | |
246 performSearch: function(query) | |
247 { | |
248 for (var i = 0; i < this._resources.length; ++i) { | |
249 var resource = this._resources[i]; | |
250 if (resource._resourcesTreeElement) | |
251 resource._resourcesTreeElement.resetBubble(); | |
252 } | |
253 | |
254 WebInspector.Panel.prototype.performSearch.call(this, query); | |
255 }, | |
256 | |
257 get visibleView() | |
258 { | |
259 if (this.visibleResource) | |
260 return this.visibleResource._resourcesView; | |
261 return null; | |
262 }, | |
263 | |
264 get calculator() | |
265 { | |
266 return this._calculator; | |
267 }, | |
268 | |
269 set calculator(x) | |
270 { | |
271 if (!x || this._calculator === x) | |
272 return; | |
273 | |
274 this._calculator = x; | |
275 this._calculator.reset(); | |
276 | |
277 this._staleResources = this._resources; | |
278 this.refresh(); | |
279 }, | |
280 | |
281 get sortingFunction() | |
282 { | |
283 return this._sortingFunction; | |
284 }, | |
285 | |
286 set sortingFunction(x) | |
287 { | |
288 this._sortingFunction = x; | |
289 this._sortResourcesIfNeeded(); | |
290 }, | |
291 | |
292 get needsRefresh() | |
293 { | |
294 return this._needsRefresh; | |
295 }, | |
296 | |
297 set needsRefresh(x) | |
298 { | |
299 if (this._needsRefresh === x) | |
300 return; | |
301 | |
302 this._needsRefresh = x; | |
303 | |
304 if (x) { | |
305 if (this.visible && !("_refreshTimeout" in this)) | |
306 this._refreshTimeout = setTimeout(this.refresh.bind(this), 500); | |
307 } else { | |
308 if ("_refreshTimeout" in this) { | |
309 clearTimeout(this._refreshTimeout); | |
310 delete this._refreshTimeout; | |
311 } | |
312 } | |
313 }, | |
314 | |
315 refreshIfNeeded: function() | |
316 { | |
317 if (this.needsRefresh) | |
318 this.refresh(); | |
319 }, | |
320 | |
321 refresh: function() | |
322 { | |
323 this.needsRefresh = false; | |
324 | |
325 var staleResourcesLength = this._staleResources.length; | |
326 var boundariesChanged = false; | |
327 | |
328 for (var i = 0; i < staleResourcesLength; ++i) { | |
329 var resource = this._staleResources[i]; | |
330 if (!resource._resourcesTreeElement) { | |
331 // Create the resource tree element and graph. | |
332 resource._resourcesTreeElement = new WebInspector.ResourceSideba
rTreeElement(resource); | |
333 resource._resourcesTreeElement._resourceGraph = new WebInspector
.ResourceGraph(resource); | |
334 | |
335 this.resourcesTreeElement.appendChild(resource._resourcesTreeEle
ment); | |
336 this.resourcesGraphsElement.appendChild(resource._resourcesTreeE
lement._resourceGraph.graphElement); | |
337 } | |
338 | |
339 resource._resourcesTreeElement.refresh(); | |
340 | |
341 if (this.calculator.updateBoundaries(resource)) | |
342 boundariesChanged = true; | |
343 } | |
344 | |
345 if (boundariesChanged) { | |
346 // The boundaries changed, so all resource graphs are stale. | |
347 this._staleResources = this._resources; | |
348 staleResourcesLength = this._staleResources.length; | |
349 } | |
350 | |
351 for (var i = 0; i < staleResourcesLength; ++i) | |
352 this._staleResources[i]._resourcesTreeElement._resourceGraph.refresh
(this.calculator); | |
353 | |
354 this._staleResources = []; | |
355 | |
356 this._updateGraphDividersIfNeeded(); | |
357 this._sortResourcesIfNeeded(); | |
358 this._updateSummaryGraph(); | |
359 }, | |
360 | |
361 reset: function() | |
362 { | |
363 this.closeVisibleResource(); | |
364 | |
365 this.containerElement.scrollTop = 0; | |
366 | |
367 delete this.currentQuery; | |
368 this.searchCanceled(); | |
369 | |
370 if (this._calculator) | |
371 this._calculator.reset(); | |
372 | |
373 if (this._resources) { | |
374 var resourcesLength = this._resources.length; | |
375 for (var i = 0; i < resourcesLength; ++i) { | |
376 var resource = this._resources[i]; | |
377 | |
378 resource.warnings = 0; | |
379 resource.errors = 0; | |
380 | |
381 delete resource._resourcesTreeElement; | |
382 delete resource._resourcesView; | |
383 } | |
384 } | |
385 | |
386 this._resources = []; | |
387 this._staleResources = []; | |
388 | |
389 this.resourcesTreeElement.removeChildren(); | |
390 this.viewsContainerElement.removeChildren(); | |
391 this.resourcesGraphsElement.removeChildren(); | |
392 this.legendElement.removeChildren(); | |
393 | |
394 this._updateGraphDividersIfNeeded(true); | |
395 | |
396 this._drawSummaryGraph(); // draws an empty graph | |
397 }, | |
398 | |
399 addResource: function(resource) | |
400 { | |
401 this._resources.push(resource); | |
402 this.refreshResource(resource); | |
403 }, | |
404 | |
405 removeResource: function(resource) | |
406 { | |
407 if (this.visibleView === resource._resourcesView) | |
408 this.closeVisibleResource(); | |
409 | |
410 this._resources.remove(resource, true); | |
411 | |
412 if (resource._resourcesTreeElement) { | |
413 this.resourcesTreeElement.removeChild(resource._resourcesTreeElement
); | |
414 this.resourcesGraphsElement.removeChild(resource._resourcesTreeEleme
nt._resourceGraph.graphElement); | |
415 } | |
416 | |
417 resource.warnings = 0; | |
418 resource.errors = 0; | |
419 | |
420 delete resource._resourcesTreeElement; | |
421 delete resource._resourcesView; | |
422 | |
423 this._adjustScrollPosition(); | |
424 }, | |
425 | |
426 addMessageToResource: function(resource, msg) | |
427 { | |
428 if (!resource) | |
429 return; | |
430 | |
431 switch (msg.level) { | |
432 case WebInspector.ConsoleMessage.MessageLevel.Warning: | |
433 resource.warnings += msg.repeatDelta; | |
434 break; | |
435 case WebInspector.ConsoleMessage.MessageLevel.Error: | |
436 resource.errors += msg.repeatDelta; | |
437 break; | |
438 } | |
439 | |
440 if (!this.currentQuery && resource._resourcesTreeElement) | |
441 resource._resourcesTreeElement.updateErrorsAndWarnings(); | |
442 | |
443 var view = this.resourceViewForResource(resource); | |
444 if (view.addMessage) | |
445 view.addMessage(msg); | |
446 }, | |
447 | |
448 clearMessages: function() | |
449 { | |
450 var resourcesLength = this._resources.length; | |
451 for (var i = 0; i < resourcesLength; ++i) { | |
452 var resource = this._resources[i]; | |
453 resource.warnings = 0; | |
454 resource.errors = 0; | |
455 | |
456 if (!this.currentQuery && resource._resourcesTreeElement) | |
457 resource._resourcesTreeElement.updateErrorsAndWarnings(); | |
458 | |
459 var view = resource._resourcesView; | |
460 if (!view || !view.clearMessages) | |
461 continue; | |
462 view.clearMessages(); | |
463 } | |
464 }, | |
465 | |
466 refreshResource: function(resource) | |
467 { | |
468 this._staleResources.push(resource); | |
469 this.needsRefresh = true; | |
470 }, | |
471 | |
472 recreateViewForResourceIfNeeded: function(resource) | |
473 { | |
474 if (!resource || !resource._resourcesView) | |
475 return; | |
476 | |
477 var newView = this._createResourceView(resource); | |
478 if (newView.prototype === resource._resourcesView.prototype) | |
479 return; | |
480 | |
481 resource.warnings = 0; | |
482 resource.errors = 0; | |
483 | |
484 if (!this.currentQuery && resource._resourcesTreeElement) | |
485 resource._resourcesTreeElement.updateErrorsAndWarnings(); | |
486 | |
487 var oldView = resource._resourcesView; | |
488 | |
489 resource._resourcesView.detach(); | |
490 delete resource._resourcesView; | |
491 | |
492 resource._resourcesView = newView; | |
493 | |
494 newView.headersVisible = oldView.headersVisible; | |
495 | |
496 if (oldView.visible && oldView.element.parentNode) | |
497 newView.show(oldView.element.parentNode); | |
498 }, | |
499 | |
500 showResource: function(resource, line) | |
501 { | |
502 if (!resource) | |
503 return; | |
504 | |
505 this.containerElement.addStyleClass("viewing-resource"); | |
506 | |
507 if (this.visibleResource && this.visibleResource._resourcesView) | |
508 this.visibleResource._resourcesView.hide(); | |
509 | |
510 var view = this.resourceViewForResource(resource); | |
511 view.headersVisible = true; | |
512 view.show(this.viewsContainerElement); | |
513 | |
514 if (line) { | |
515 if (view.revealLine) | |
516 view.revealLine(line); | |
517 if (view.highlightLine) | |
518 view.highlightLine(line); | |
519 } | |
520 | |
521 if (resource._resourcesTreeElement) { | |
522 resource._resourcesTreeElement.reveal(); | |
523 resource._resourcesTreeElement.select(true); | |
524 } | |
525 | |
526 this.visibleResource = resource; | |
527 | |
528 this._updateSidebarWidth(); | |
529 }, | |
530 | |
531 showView: function(view) | |
532 { | |
533 if (!view) | |
534 return; | |
535 this.showResource(view.resource); | |
536 }, | |
537 | |
538 closeVisibleResource: function() | |
539 { | |
540 this.containerElement.removeStyleClass("viewing-resource"); | |
541 this._updateDividersLabelBarPosition(); | |
542 | |
543 if (this.visibleResource && this.visibleResource._resourcesView) | |
544 this.visibleResource._resourcesView.hide(); | |
545 delete this.visibleResource; | |
546 | |
547 if (this._lastSelectedGraphTreeElement) | |
548 this._lastSelectedGraphTreeElement.select(true); | |
549 | |
550 this._updateSidebarWidth(); | |
551 }, | |
552 | |
553 resourceViewForResource: function(resource) | |
554 { | |
555 if (!resource) | |
556 return null; | |
557 if (!resource._resourcesView) | |
558 resource._resourcesView = this._createResourceView(resource); | |
559 return resource._resourcesView; | |
560 }, | |
561 | |
562 sourceFrameForResource: function(resource) | |
563 { | |
564 var view = this.resourceViewForResource(resource); | |
565 if (!view) | |
566 return null; | |
567 | |
568 if (!view.setupSourceFrameIfNeeded) | |
569 return null; | |
570 | |
571 // Setting up the source frame requires that we be attached. | |
572 if (!this.element.parentNode) | |
573 this.attach(); | |
574 | |
575 view.setupSourceFrameIfNeeded(); | |
576 return view.sourceFrame; | |
577 }, | |
578 | |
579 handleKeyEvent: function(event) | |
580 { | |
581 this.sidebarTree.handleKeyEvent(event); | |
582 }, | |
583 | |
584 _makeLegendElement: function(label, value, color) | |
585 { | |
586 var legendElement = document.createElement("label"); | |
587 legendElement.className = "resources-graph-legend-item"; | |
588 | |
589 if (color) { | |
590 var swatch = document.createElement("canvas"); | |
591 swatch.className = "resources-graph-legend-swatch"; | |
592 swatch.setAttribute("width", "13"); | |
593 swatch.setAttribute("height", "24"); | |
594 | |
595 legendElement.appendChild(swatch); | |
596 | |
597 this._drawSwatch(swatch, color); | |
598 } | |
599 | |
600 var labelElement = document.createElement("div"); | |
601 labelElement.className = "resources-graph-legend-label"; | |
602 legendElement.appendChild(labelElement); | |
603 | |
604 var headerElement = document.createElement("div"); | |
605 var headerElement = document.createElement("div"); | |
606 headerElement.className = "resources-graph-legend-header"; | |
607 headerElement.textContent = label; | |
608 labelElement.appendChild(headerElement); | |
609 | |
610 var valueElement = document.createElement("div"); | |
611 valueElement.className = "resources-graph-legend-value"; | |
612 valueElement.textContent = value; | |
613 labelElement.appendChild(valueElement); | |
614 | |
615 return legendElement; | |
616 }, | |
617 | |
618 _sortResourcesIfNeeded: function() | |
619 { | |
620 var sortedElements = [].concat(this.resourcesTreeElement.children); | |
621 sortedElements.sort(this.sortingFunction); | |
622 | |
623 var sortedElementsLength = sortedElements.length; | |
624 for (var i = 0; i < sortedElementsLength; ++i) { | |
625 var treeElement = sortedElements[i]; | |
626 if (treeElement === this.resourcesTreeElement.children[i]) | |
627 continue; | |
628 | |
629 var wasSelected = treeElement.selected; | |
630 this.resourcesTreeElement.removeChild(treeElement); | |
631 this.resourcesTreeElement.insertChild(treeElement, i); | |
632 if (wasSelected) | |
633 treeElement.select(true); | |
634 | |
635 var graphElement = treeElement._resourceGraph.graphElement; | |
636 this.resourcesGraphsElement.insertBefore(graphElement, this.resource
sGraphsElement.children[i]); | |
637 } | |
638 }, | |
639 | |
640 _updateGraphDividersIfNeeded: function(force) | |
641 { | |
642 if (!this.visible) { | |
643 this.needsRefresh = true; | |
644 return; | |
645 } | |
646 | |
647 if (document.body.offsetWidth <= 0) { | |
648 // The stylesheet hasn't loaded yet or the window is closed, | |
649 // so we can't calculate what is need. Return early. | |
650 return; | |
651 } | |
652 | |
653 var dividerCount = Math.round(this.dividersElement.offsetWidth / 64); | |
654 var slice = this.calculator.boundarySpan / dividerCount; | |
655 if (!force && this._currentDividerSlice === slice) | |
656 return; | |
657 | |
658 this._currentDividerSlice = slice; | |
659 | |
660 this.dividersElement.removeChildren(); | |
661 this.dividersLabelBarElement.removeChildren(); | |
662 | |
663 for (var i = 1; i <= dividerCount; ++i) { | |
664 var divider = document.createElement("div"); | |
665 divider.className = "resources-divider"; | |
666 if (i === dividerCount) | |
667 divider.addStyleClass("last"); | |
668 divider.style.left = ((i / dividerCount) * 100) + "%"; | |
669 | |
670 this.dividersElement.appendChild(divider.cloneNode()); | |
671 | |
672 var label = document.createElement("div"); | |
673 label.className = "resources-divider-label"; | |
674 if (!isNaN(slice)) | |
675 label.textContent = this.calculator.formatValue(slice * i); | |
676 divider.appendChild(label); | |
677 | |
678 this.dividersLabelBarElement.appendChild(divider); | |
679 } | |
680 }, | |
681 | |
682 _fadeOutRect: function(ctx, x, y, w, h, a1, a2) | |
683 { | |
684 ctx.save(); | |
685 | |
686 var gradient = ctx.createLinearGradient(x, y, x, y + h); | |
687 gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")"); | |
688 gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")"); | |
689 gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)"); | |
690 | |
691 ctx.globalCompositeOperation = "destination-out"; | |
692 | |
693 ctx.fillStyle = gradient; | |
694 ctx.fillRect(x, y, w, h); | |
695 | |
696 ctx.restore(); | |
697 }, | |
698 | |
699 _drawSwatch: function(canvas, color) | |
700 { | |
701 var ctx = canvas.getContext("2d"); | |
702 | |
703 function drawSwatchSquare() { | |
704 ctx.fillStyle = color; | |
705 ctx.fillRect(0, 0, 13, 13); | |
706 | |
707 var gradient = ctx.createLinearGradient(0, 0, 13, 13); | |
708 gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)"); | |
709 gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); | |
710 | |
711 ctx.fillStyle = gradient; | |
712 ctx.fillRect(0, 0, 13, 13); | |
713 | |
714 gradient = ctx.createLinearGradient(13, 13, 0, 0); | |
715 gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)"); | |
716 gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)"); | |
717 | |
718 ctx.fillStyle = gradient; | |
719 ctx.fillRect(0, 0, 13, 13); | |
720 | |
721 ctx.strokeStyle = "rgba(0, 0, 0, 0.6)"; | |
722 ctx.strokeRect(0.5, 0.5, 12, 12); | |
723 } | |
724 | |
725 ctx.clearRect(0, 0, 13, 24); | |
726 | |
727 drawSwatchSquare(); | |
728 | |
729 ctx.save(); | |
730 | |
731 ctx.translate(0, 25); | |
732 ctx.scale(1, -1); | |
733 | |
734 drawSwatchSquare(); | |
735 | |
736 ctx.restore(); | |
737 | |
738 this._fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0); | |
739 }, | |
740 | |
741 _drawSummaryGraph: function(segments) | |
742 { | |
743 if (!this.summaryGraphElement) | |
744 return; | |
745 | |
746 if (!segments || !segments.length) { | |
747 segments = [{color: "white", value: 1}]; | |
748 this._showingEmptySummaryGraph = true; | |
749 } else | |
750 delete this._showingEmptySummaryGraph; | |
751 | |
752 // Calculate the total of all segments. | |
753 var total = 0; | |
754 for (var i = 0; i < segments.length; ++i) | |
755 total += segments[i].value; | |
756 | |
757 // Calculate the percentage of each segment, rounded to the nearest perc
ent. | |
758 var percents = segments.map(function(s) { return Math.max(Math.round(100
* s.value / total), 1) }); | |
759 | |
760 // Calculate the total percentage. | |
761 var percentTotal = 0; | |
762 for (var i = 0; i < percents.length; ++i) | |
763 percentTotal += percents[i]; | |
764 | |
765 // Make sure our percentage total is not greater-than 100, it can be gre
ater | |
766 // if we rounded up for a few segments. | |
767 while (percentTotal > 100) { | |
768 for (var i = 0; i < percents.length && percentTotal > 100; ++i) { | |
769 if (percents[i] > 1) { | |
770 --percents[i]; | |
771 --percentTotal; | |
772 } | |
773 } | |
774 } | |
775 | |
776 // Make sure our percentage total is not less-than 100, it can be less | |
777 // if we rounded down for a few segments. | |
778 while (percentTotal < 100) { | |
779 for (var i = 0; i < percents.length && percentTotal < 100; ++i) { | |
780 ++percents[i]; | |
781 ++percentTotal; | |
782 } | |
783 } | |
784 | |
785 var ctx = this.summaryGraphElement.getContext("2d"); | |
786 | |
787 var x = 0; | |
788 var y = 0; | |
789 var w = 450; | |
790 var h = 19; | |
791 var r = (h / 2); | |
792 | |
793 function drawPillShadow() | |
794 { | |
795 // This draws a line with a shadow that is offset away from the line
. The line is stroked | |
796 // twice with different X shadow offsets to give more feathered edge
s. Later we erase the | |
797 // line with destination-out 100% transparent black, leaving only th
e shadow. This only | |
798 // works if nothing has been drawn into the canvas yet. | |
799 | |
800 ctx.beginPath(); | |
801 ctx.moveTo(x + 4, y + h - 3 - 0.5); | |
802 ctx.lineTo(x + w - 4, y + h - 3 - 0.5); | |
803 ctx.closePath(); | |
804 | |
805 ctx.save(); | |
806 | |
807 ctx.shadowBlur = 2; | |
808 ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; | |
809 ctx.shadowOffsetX = 3; | |
810 ctx.shadowOffsetY = 5; | |
811 | |
812 ctx.strokeStyle = "white"; | |
813 ctx.lineWidth = 1; | |
814 | |
815 ctx.stroke(); | |
816 | |
817 ctx.shadowOffsetX = -3; | |
818 | |
819 ctx.stroke(); | |
820 | |
821 ctx.restore(); | |
822 | |
823 ctx.save(); | |
824 | |
825 ctx.globalCompositeOperation = "destination-out"; | |
826 ctx.strokeStyle = "rgba(0, 0, 0, 1)"; | |
827 ctx.lineWidth = 1; | |
828 | |
829 ctx.stroke(); | |
830 | |
831 ctx.restore(); | |
832 } | |
833 | |
834 function drawPill() | |
835 { | |
836 // Make a rounded rect path. | |
837 ctx.beginPath(); | |
838 ctx.moveTo(x, y + r); | |
839 ctx.lineTo(x, y + h - r); | |
840 ctx.quadraticCurveTo(x, y + h, x + r, y + h); | |
841 ctx.lineTo(x + w - r, y + h); | |
842 ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r); | |
843 ctx.lineTo(x + w, y + r); | |
844 ctx.quadraticCurveTo(x + w, y, x + w - r, y); | |
845 ctx.lineTo(x + r, y); | |
846 ctx.quadraticCurveTo(x, y, x, y + r); | |
847 ctx.closePath(); | |
848 | |
849 // Clip to the rounded rect path. | |
850 ctx.save(); | |
851 ctx.clip(); | |
852 | |
853 // Fill the segments with the associated color. | |
854 var previousSegmentsWidth = 0; | |
855 for (var i = 0; i < segments.length; ++i) { | |
856 var segmentWidth = Math.round(w * percents[i] / 100); | |
857 ctx.fillStyle = segments[i].color; | |
858 ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h); | |
859 previousSegmentsWidth += segmentWidth; | |
860 } | |
861 | |
862 // Draw the segment divider lines. | |
863 ctx.lineWidth = 1; | |
864 for (var i = 1; i < 20; ++i) { | |
865 ctx.beginPath(); | |
866 ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y); | |
867 ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h); | |
868 ctx.closePath(); | |
869 | |
870 ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"; | |
871 ctx.stroke(); | |
872 | |
873 ctx.beginPath(); | |
874 ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y); | |
875 ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h); | |
876 ctx.closePath(); | |
877 | |
878 ctx.strokeStyle = "rgba(255, 255, 255, 0.2)"; | |
879 ctx.stroke(); | |
880 } | |
881 | |
882 // Draw the pill shading. | |
883 var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5))
; | |
884 lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)"); | |
885 lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)"); | |
886 lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); | |
887 | |
888 var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h
); | |
889 darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)"); | |
890 darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)"); | |
891 darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)"); | |
892 | |
893 ctx.fillStyle = darkGradient; | |
894 ctx.fillRect(x, y, w, h); | |
895 | |
896 ctx.fillStyle = lightGradient; | |
897 ctx.fillRect(x, y, w, h); | |
898 | |
899 ctx.restore(); | |
900 } | |
901 | |
902 ctx.clearRect(x, y, w, (h * 2)); | |
903 | |
904 drawPillShadow(); | |
905 drawPill(); | |
906 | |
907 ctx.save(); | |
908 | |
909 ctx.translate(0, (h * 2) + 1); | |
910 ctx.scale(1, -1); | |
911 | |
912 drawPill(); | |
913 | |
914 ctx.restore(); | |
915 | |
916 this._fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0); | |
917 }, | |
918 | |
919 _updateSummaryGraph: function() | |
920 { | |
921 var graphInfo = this.calculator.computeSummaryValues(this._resources); | |
922 | |
923 var categoryOrder = ["documents", "stylesheets", "images", "scripts", "x
hr", "fonts", "other"]; | |
924 var categoryColors = {documents: {r: 47, g: 102, b: 236}, stylesheets: {
r: 157, g: 231, b: 119}, images: {r: 164, g: 60, b: 255}, scripts: {r: 255, g: 1
21, b: 0}, xhr: {r: 231, g: 231, b: 10}, fonts: {r: 255, g: 82, b: 62}, other: {
r: 186, g: 186, b: 186}}; | |
925 var fillSegments = []; | |
926 | |
927 this.legendElement.removeChildren(); | |
928 | |
929 for (var i = 0; i < categoryOrder.length; ++i) { | |
930 var category = categoryOrder[i]; | |
931 var size = graphInfo.categoryValues[category]; | |
932 if (!size) | |
933 continue; | |
934 | |
935 var color = categoryColors[category]; | |
936 var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b
+ ")"; | |
937 | |
938 var fillSegment = {color: colorString, value: size}; | |
939 fillSegments.push(fillSegment); | |
940 | |
941 var legendLabel = this._makeLegendElement(WebInspector.resourceCateg
ories[category].title, this.calculator.formatValue(size), colorString); | |
942 this.legendElement.appendChild(legendLabel); | |
943 } | |
944 | |
945 if (graphInfo.total) { | |
946 var totalLegendLabel = this._makeLegendElement(WebInspector.UIString
("Total"), this.calculator.formatValue(graphInfo.total)); | |
947 totalLegendLabel.addStyleClass("total"); | |
948 this.legendElement.appendChild(totalLegendLabel); | |
949 } | |
950 | |
951 this._drawSummaryGraph(fillSegments); | |
952 }, | |
953 | |
954 _updateDividersLabelBarPosition: function() | |
955 { | |
956 var scrollTop = this.containerElement.scrollTop; | |
957 var dividersTop = (scrollTop < this.summaryElement.offsetHeight ? this.s
ummaryElement.offsetHeight : scrollTop); | |
958 this.dividersElement.style.top = scrollTop + "px"; | |
959 this.dividersLabelBarElement.style.top = dividersTop + "px"; | |
960 }, | |
961 | |
962 _graphSelected: function(treeElement) | |
963 { | |
964 if (this._lastSelectedGraphTreeElement) | |
965 this._lastSelectedGraphTreeElement.selectedSortingOptionIndex = this
.sortingSelectElement.selectedIndex; | |
966 | |
967 this._lastSelectedGraphTreeElement = treeElement; | |
968 | |
969 this.sortingSelectElement.removeChildren(); | |
970 for (var i = 0; i < treeElement.sortingOptions.length; ++i) { | |
971 var sortingOption = treeElement.sortingOptions[i]; | |
972 var option = document.createElement("option"); | |
973 option.label = sortingOption.name; | |
974 option.sortingFunction = sortingOption.sortingFunction; | |
975 option.calculator = sortingOption.calculator; | |
976 this.sortingSelectElement.appendChild(option); | |
977 } | |
978 | |
979 this.sortingSelectElement.selectedIndex = treeElement.selectedSortingOpt
ionIndex; | |
980 this._changeSortingFunction(); | |
981 | |
982 this.closeVisibleResource(); | |
983 this.containerElement.scrollTop = 0; | |
984 }, | |
985 | |
986 _toggleLargerResources: function() | |
987 { | |
988 if (!this.resourcesTreeElement._childrenListNode) | |
989 return; | |
990 | |
991 this.resourcesTreeElement.smallChildren = !this.resourcesTreeElement.sma
llChildren; | |
992 | |
993 if (this.resourcesTreeElement.smallChildren) { | |
994 this.resourcesGraphsElement.addStyleClass("small"); | |
995 this.largerResourcesButton.title = WebInspector.UIString("Use large
resource rows."); | |
996 this.largerResourcesButton.removeStyleClass("toggled-on"); | |
997 this._adjustScrollPosition(); | |
998 } else { | |
999 this.resourcesGraphsElement.removeStyleClass("small"); | |
1000 this.largerResourcesButton.title = WebInspector.UIString("Use small
resource rows."); | |
1001 this.largerResourcesButton.addStyleClass("toggled-on"); | |
1002 } | |
1003 }, | |
1004 | |
1005 _adjustScrollPosition: function() | |
1006 { | |
1007 // Prevent the container from being scrolled off the end. | |
1008 if ((this.containerElement.scrollTop + this.containerElement.offsetHeigh
t) > this.sidebarElement.offsetHeight) | |
1009 this.containerElement.scrollTop = (this.sidebarElement.offsetHeight
- this.containerElement.offsetHeight); | |
1010 }, | |
1011 | |
1012 _changeSortingFunction: function() | |
1013 { | |
1014 var selectedOption = this.sortingSelectElement[this.sortingSelectElement
.selectedIndex]; | |
1015 this.sortingFunction = selectedOption.sortingFunction; | |
1016 this.calculator = selectedOption.calculator; | |
1017 }, | |
1018 | |
1019 _createResourceView: function(resource) | |
1020 { | |
1021 switch (resource.category) { | |
1022 case WebInspector.resourceCategories.documents: | |
1023 case WebInspector.resourceCategories.stylesheets: | |
1024 case WebInspector.resourceCategories.scripts: | |
1025 case WebInspector.resourceCategories.xhr: | |
1026 return new WebInspector.SourceView(resource); | |
1027 case WebInspector.resourceCategories.images: | |
1028 return new WebInspector.ImageView(resource); | |
1029 case WebInspector.resourceCategories.fonts: | |
1030 return new WebInspector.FontView(resource); | |
1031 default: | |
1032 return new WebInspector.ResourceView(resource); | |
1033 } | |
1034 }, | |
1035 | |
1036 _startSidebarDragging: function(event) | |
1037 { | |
1038 WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDr
agging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); | |
1039 }, | |
1040 | |
1041 _sidebarDragging: function(event) | |
1042 { | |
1043 this._updateSidebarWidth(event.pageX); | |
1044 | |
1045 event.preventDefault(); | |
1046 }, | |
1047 | |
1048 _endSidebarDragging: function(event) | |
1049 { | |
1050 WebInspector.elementDragEnd(event); | |
1051 }, | |
1052 | |
1053 _updateSidebarWidth: function(width) | |
1054 { | |
1055 if (this.sidebarElement.offsetWidth <= 0) { | |
1056 // The stylesheet hasn't loaded yet or the window is closed, | |
1057 // so we can't calculate what is need. Return early. | |
1058 return; | |
1059 } | |
1060 | |
1061 if (!("_currentSidebarWidth" in this)) | |
1062 this._currentSidebarWidth = this.sidebarElement.offsetWidth; | |
1063 | |
1064 if (typeof width === "undefined") | |
1065 width = this._currentSidebarWidth; | |
1066 | |
1067 width = Number.constrain(width, Preferences.minSidebarWidth, window.inne
rWidth / 2); | |
1068 | |
1069 this._currentSidebarWidth = width; | |
1070 | |
1071 if (this.visibleResource) { | |
1072 this.containerElement.style.width = width + "px"; | |
1073 this.sidebarElement.style.removeProperty("width"); | |
1074 } else { | |
1075 this.sidebarElement.style.width = width + "px"; | |
1076 this.containerElement.style.removeProperty("width"); | |
1077 } | |
1078 | |
1079 this.containerContentElement.style.left = width + "px"; | |
1080 this.viewsContainerElement.style.left = width + "px"; | |
1081 this.sidebarResizeElement.style.left = (width - 3) + "px"; | |
1082 | |
1083 this._updateGraphDividersIfNeeded(); | |
1084 | |
1085 var visibleView = this.visibleView; | |
1086 if (visibleView && "resize" in visibleView) | |
1087 visibleView.resize(); | |
1088 } | |
1089 } | |
1090 | |
1091 WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.Panel.prototype; | |
1092 | |
1093 WebInspector.ResourceCalculator = function() | |
1094 { | |
1095 } | |
1096 | |
1097 WebInspector.ResourceCalculator.prototype = { | |
1098 computeSummaryValues: function(resources) | |
1099 { | |
1100 var total = 0; | |
1101 var categoryValues = {}; | |
1102 | |
1103 var resourcesLength = resources.length; | |
1104 for (var i = 0; i < resourcesLength; ++i) { | |
1105 var resource = resources[i]; | |
1106 var value = this._value(resource); | |
1107 if (typeof value === "undefined") | |
1108 continue; | |
1109 if (!(resource.category.name in categoryValues)) | |
1110 categoryValues[resource.category.name] = 0; | |
1111 categoryValues[resource.category.name] += value; | |
1112 total += value; | |
1113 } | |
1114 | |
1115 return {categoryValues: categoryValues, total: total}; | |
1116 }, | |
1117 | |
1118 computeBarGraphPercentages: function(resource) | |
1119 { | |
1120 return {start: 0, middle: 0, end: (this._value(resource) / this.boundary
Span) * 100}; | |
1121 }, | |
1122 | |
1123 computeBarGraphLabels: function(resource) | |
1124 { | |
1125 const label = this.formatValue(this._value(resource)); | |
1126 var tooltip = label; | |
1127 if (resource.cached) | |
1128 tooltip = WebInspector.UIString("%s (from cache)", tooltip); | |
1129 return {left: label, right: label, tooltip: tooltip}; | |
1130 }, | |
1131 | |
1132 get boundarySpan() | |
1133 { | |
1134 return this.maximumBoundary - this.minimumBoundary; | |
1135 }, | |
1136 | |
1137 updateBoundaries: function(resource) | |
1138 { | |
1139 this.minimumBoundary = 0; | |
1140 | |
1141 var value = this._value(resource); | |
1142 if (typeof this.maximumBoundary === "undefined" || value > this.maximumB
oundary) { | |
1143 this.maximumBoundary = value; | |
1144 return true; | |
1145 } | |
1146 | |
1147 return false; | |
1148 }, | |
1149 | |
1150 reset: function() | |
1151 { | |
1152 delete this.minimumBoundary; | |
1153 delete this.maximumBoundary; | |
1154 }, | |
1155 | |
1156 _value: function(resource) | |
1157 { | |
1158 return 0; | |
1159 }, | |
1160 | |
1161 formatValue: function(value) | |
1162 { | |
1163 return value.toString(); | |
1164 } | |
1165 } | |
1166 | |
1167 WebInspector.ResourceTimeCalculator = function(startAtZero) | |
1168 { | |
1169 WebInspector.ResourceCalculator.call(this); | |
1170 this.startAtZero = startAtZero; | |
1171 } | |
1172 | |
1173 WebInspector.ResourceTimeCalculator.prototype = { | |
1174 computeSummaryValues: function(resources) | |
1175 { | |
1176 var resourcesByCategory = {}; | |
1177 var resourcesLength = resources.length; | |
1178 for (var i = 0; i < resourcesLength; ++i) { | |
1179 var resource = resources[i]; | |
1180 if (!(resource.category.name in resourcesByCategory)) | |
1181 resourcesByCategory[resource.category.name] = []; | |
1182 resourcesByCategory[resource.category.name].push(resource); | |
1183 } | |
1184 | |
1185 var earliestStart; | |
1186 var latestEnd; | |
1187 var categoryValues = {}; | |
1188 for (var category in resourcesByCategory) { | |
1189 resourcesByCategory[category].sort(WebInspector.Resource.CompareByTi
me); | |
1190 categoryValues[category] = 0; | |
1191 | |
1192 var segment = {start: -1, end: -1}; | |
1193 | |
1194 var categoryResources = resourcesByCategory[category]; | |
1195 var resourcesLength = categoryResources.length; | |
1196 for (var i = 0; i < resourcesLength; ++i) { | |
1197 var resource = categoryResources[i]; | |
1198 if (resource.startTime === -1 || resource.endTime === -1) | |
1199 continue; | |
1200 | |
1201 if (typeof earliestStart === "undefined") | |
1202 earliestStart = resource.startTime; | |
1203 else | |
1204 earliestStart = Math.min(earliestStart, resource.startTime); | |
1205 | |
1206 if (typeof latestEnd === "undefined") | |
1207 latestEnd = resource.endTime; | |
1208 else | |
1209 latestEnd = Math.max(latestEnd, resource.endTime); | |
1210 | |
1211 if (resource.startTime <= segment.end) { | |
1212 segment.end = Math.max(segment.end, resource.endTime); | |
1213 continue; | |
1214 } | |
1215 | |
1216 categoryValues[category] += segment.end - segment.start; | |
1217 | |
1218 segment.start = resource.startTime; | |
1219 segment.end = resource.endTime; | |
1220 } | |
1221 | |
1222 // Add the last segment | |
1223 categoryValues[category] += segment.end - segment.start; | |
1224 } | |
1225 | |
1226 return {categoryValues: categoryValues, total: latestEnd - earliestStart
}; | |
1227 }, | |
1228 | |
1229 computeBarGraphPercentages: function(resource) | |
1230 { | |
1231 if (resource.startTime !== -1) | |
1232 var start = ((resource.startTime - this.minimumBoundary) / this.boun
darySpan) * 100; | |
1233 else | |
1234 var start = 0; | |
1235 | |
1236 if (resource.responseReceivedTime !== -1) | |
1237 var middle = ((resource.responseReceivedTime - this.minimumBoundary)
/ this.boundarySpan) * 100; | |
1238 else | |
1239 var middle = (this.startAtZero ? start : 100); | |
1240 | |
1241 if (resource.endTime !== -1) | |
1242 var end = ((resource.endTime - this.minimumBoundary) / this.boundary
Span) * 100; | |
1243 else | |
1244 var end = (this.startAtZero ? middle : 100); | |
1245 | |
1246 if (this.startAtZero) { | |
1247 end -= start; | |
1248 middle -= start; | |
1249 start = 0; | |
1250 } | |
1251 | |
1252 return {start: start, middle: middle, end: end}; | |
1253 }, | |
1254 | |
1255 computeBarGraphLabels: function(resource) | |
1256 { | |
1257 var leftLabel = ""; | |
1258 if (resource.latency > 0) | |
1259 leftLabel = this.formatValue(resource.latency); | |
1260 | |
1261 var rightLabel = ""; | |
1262 if (resource.responseReceivedTime !== -1 && resource.endTime !== -1) | |
1263 rightLabel = this.formatValue(resource.endTime - resource.responseRe
ceivedTime); | |
1264 | |
1265 if (leftLabel && rightLabel) { | |
1266 var total = this.formatValue(resource.duration); | |
1267 var tooltip = WebInspector.UIString("%s latency, %s download (%s tot
al)", leftLabel, rightLabel, total); | |
1268 } else if (leftLabel) | |
1269 var tooltip = WebInspector.UIString("%s latency", leftLabel); | |
1270 else if (rightLabel) | |
1271 var tooltip = WebInspector.UIString("%s download", rightLabel); | |
1272 | |
1273 if (resource.cached) | |
1274 tooltip = WebInspector.UIString("%s (from cache)", tooltip); | |
1275 | |
1276 return {left: leftLabel, right: rightLabel, tooltip: tooltip}; | |
1277 }, | |
1278 | |
1279 updateBoundaries: function(resource) | |
1280 { | |
1281 var didChange = false; | |
1282 | |
1283 var lowerBound; | |
1284 if (this.startAtZero) | |
1285 lowerBound = 0; | |
1286 else | |
1287 lowerBound = this._lowerBound(resource); | |
1288 | |
1289 if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" ||
lowerBound < this.minimumBoundary)) { | |
1290 this.minimumBoundary = lowerBound; | |
1291 didChange = true; | |
1292 } | |
1293 | |
1294 var upperBound = this._upperBound(resource); | |
1295 if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" ||
upperBound > this.maximumBoundary)) { | |
1296 this.maximumBoundary = upperBound; | |
1297 didChange = true; | |
1298 } | |
1299 | |
1300 return didChange; | |
1301 }, | |
1302 | |
1303 formatValue: function(value) | |
1304 { | |
1305 return Number.secondsToString(value, WebInspector.UIString.bind(WebInspe
ctor)); | |
1306 }, | |
1307 | |
1308 _lowerBound: function(resource) | |
1309 { | |
1310 return 0; | |
1311 }, | |
1312 | |
1313 _upperBound: function(resource) | |
1314 { | |
1315 return 0; | |
1316 }, | |
1317 } | |
1318 | |
1319 WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.ResourceC
alculator.prototype; | |
1320 | |
1321 WebInspector.ResourceTransferTimeCalculator = function() | |
1322 { | |
1323 WebInspector.ResourceTimeCalculator.call(this, false); | |
1324 } | |
1325 | |
1326 WebInspector.ResourceTransferTimeCalculator.prototype = { | |
1327 formatValue: function(value) | |
1328 { | |
1329 return Number.secondsToString(value, WebInspector.UIString.bind(WebInspe
ctor)); | |
1330 }, | |
1331 | |
1332 _lowerBound: function(resource) | |
1333 { | |
1334 return resource.startTime; | |
1335 }, | |
1336 | |
1337 _upperBound: function(resource) | |
1338 { | |
1339 return resource.endTime; | |
1340 } | |
1341 } | |
1342 | |
1343 WebInspector.ResourceTransferTimeCalculator.prototype.__proto__ = WebInspector.R
esourceTimeCalculator.prototype; | |
1344 | |
1345 WebInspector.ResourceTransferDurationCalculator = function() | |
1346 { | |
1347 WebInspector.ResourceTimeCalculator.call(this, true); | |
1348 } | |
1349 | |
1350 WebInspector.ResourceTransferDurationCalculator.prototype = { | |
1351 formatValue: function(value) | |
1352 { | |
1353 return Number.secondsToString(value, WebInspector.UIString.bind(WebInspe
ctor)); | |
1354 }, | |
1355 | |
1356 _upperBound: function(resource) | |
1357 { | |
1358 return resource.duration; | |
1359 } | |
1360 } | |
1361 | |
1362 WebInspector.ResourceTransferDurationCalculator.prototype.__proto__ = WebInspect
or.ResourceTimeCalculator.prototype; | |
1363 | |
1364 WebInspector.ResourceTransferSizeCalculator = function() | |
1365 { | |
1366 WebInspector.ResourceCalculator.call(this); | |
1367 } | |
1368 | |
1369 WebInspector.ResourceTransferSizeCalculator.prototype = { | |
1370 _value: function(resource) | |
1371 { | |
1372 return resource.contentLength; | |
1373 }, | |
1374 | |
1375 formatValue: function(value) | |
1376 { | |
1377 return Number.bytesToString(value, WebInspector.UIString.bind(WebInspect
or)); | |
1378 } | |
1379 } | |
1380 | |
1381 WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.R
esourceCalculator.prototype; | |
1382 | |
1383 WebInspector.ResourceSidebarTreeElement = function(resource) | |
1384 { | |
1385 this.resource = resource; | |
1386 | |
1387 this.createIconElement(); | |
1388 | |
1389 WebInspector.SidebarTreeElement.call(this, "resource-sidebar-tree-item", "",
"", resource); | |
1390 | |
1391 this.refreshTitles(); | |
1392 } | |
1393 | |
1394 WebInspector.ResourceSidebarTreeElement.prototype = { | |
1395 onattach: function() | |
1396 { | |
1397 WebInspector.SidebarTreeElement.prototype.onattach.call(this); | |
1398 | |
1399 this._listItemNode.addStyleClass("resources-category-" + this.resource.c
ategory.name); | |
1400 }, | |
1401 | |
1402 onselect: function() | |
1403 { | |
1404 WebInspector.panels.resources.showResource(this.resource); | |
1405 }, | |
1406 | |
1407 get mainTitle() | |
1408 { | |
1409 return this.resource.displayName; | |
1410 }, | |
1411 | |
1412 set mainTitle(x) | |
1413 { | |
1414 // Do nothing. | |
1415 }, | |
1416 | |
1417 get subtitle() | |
1418 { | |
1419 var subtitle = this.resource.displayDomain; | |
1420 | |
1421 if (this.resource.path && this.resource.lastPathComponent) { | |
1422 var lastPathComponentIndex = this.resource.path.lastIndexOf("/" + th
is.resource.lastPathComponent); | |
1423 if (lastPathComponentIndex != -1) | |
1424 subtitle += this.resource.path.substring(0, lastPathComponentInd
ex); | |
1425 } | |
1426 | |
1427 return subtitle; | |
1428 }, | |
1429 | |
1430 set subtitle(x) | |
1431 { | |
1432 // Do nothing. | |
1433 }, | |
1434 | |
1435 createIconElement: function() | |
1436 { | |
1437 var previousIconElement = this.iconElement; | |
1438 | |
1439 if (this.resource.category === WebInspector.resourceCategories.images) { | |
1440 var previewImage = document.createElement("img"); | |
1441 previewImage.className = "image-resource-icon-preview"; | |
1442 previewImage.src = this.resource.url; | |
1443 | |
1444 this.iconElement = document.createElement("div"); | |
1445 this.iconElement.className = "icon"; | |
1446 this.iconElement.appendChild(previewImage); | |
1447 } else { | |
1448 this.iconElement = document.createElement("img"); | |
1449 this.iconElement.className = "icon"; | |
1450 } | |
1451 | |
1452 if (previousIconElement) | |
1453 previousIconElement.parentNode.replaceChild(this.iconElement, previo
usIconElement); | |
1454 }, | |
1455 | |
1456 refresh: function() | |
1457 { | |
1458 this.refreshTitles(); | |
1459 | |
1460 if (!this._listItemNode.hasStyleClass("resources-category-" + this.resou
rce.category.name)) { | |
1461 this._listItemNode.removeMatchingStyleClasses("resources-category-\\
w+"); | |
1462 this._listItemNode.addStyleClass("resources-category-" + this.resour
ce.category.name); | |
1463 | |
1464 this.createIconElement(); | |
1465 } | |
1466 }, | |
1467 | |
1468 resetBubble: function() | |
1469 { | |
1470 this.bubbleText = ""; | |
1471 this.bubbleElement.removeStyleClass("search-matches"); | |
1472 this.bubbleElement.removeStyleClass("warning"); | |
1473 this.bubbleElement.removeStyleClass("error"); | |
1474 }, | |
1475 | |
1476 set searchMatches(matches) | |
1477 { | |
1478 this.resetBubble(); | |
1479 | |
1480 if (!matches) | |
1481 return; | |
1482 | |
1483 this.bubbleText = matches; | |
1484 this.bubbleElement.addStyleClass("search-matches"); | |
1485 }, | |
1486 | |
1487 updateErrorsAndWarnings: function() | |
1488 { | |
1489 this.resetBubble(); | |
1490 | |
1491 if (this.resource.warnings || this.resource.errors) | |
1492 this.bubbleText = (this.resource.warnings + this.resource.errors); | |
1493 | |
1494 if (this.resource.warnings) | |
1495 this.bubbleElement.addStyleClass("warning"); | |
1496 | |
1497 if (this.resource.errors) | |
1498 this.bubbleElement.addStyleClass("error"); | |
1499 } | |
1500 } | |
1501 | |
1502 WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime = function(a
, b) | |
1503 { | |
1504 return WebInspector.Resource.CompareByStartTime(a.resource, b.resource) | |
1505 || WebInspector.Resource.CompareByEndTime(a.resource, b.resource) | |
1506 || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.res
ource); | |
1507 } | |
1508 | |
1509 WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime =
function(a, b) | |
1510 { | |
1511 return WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.res
ource) | |
1512 || WebInspector.Resource.CompareByStartTime(a.resource, b.resource) | |
1513 || WebInspector.Resource.CompareByEndTime(a.resource, b.resource); | |
1514 } | |
1515 | |
1516 WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime = function(a,
b) | |
1517 { | |
1518 return WebInspector.Resource.CompareByEndTime(a.resource, b.resource) | |
1519 || WebInspector.Resource.CompareByStartTime(a.resource, b.resource) | |
1520 || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.res
ource); | |
1521 } | |
1522 | |
1523 WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration = function(a
, b) | |
1524 { | |
1525 return -1 * WebInspector.Resource.CompareByDuration(a.resource, b.resource); | |
1526 } | |
1527 | |
1528 WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency = function(a,
b) | |
1529 { | |
1530 return -1 * WebInspector.Resource.CompareByLatency(a.resource, b.resource); | |
1531 } | |
1532 | |
1533 WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize = function(a, b) | |
1534 { | |
1535 return -1 * WebInspector.Resource.CompareBySize(a.resource, b.resource); | |
1536 } | |
1537 | |
1538 WebInspector.ResourceSidebarTreeElement.prototype.__proto__ = WebInspector.Sideb
arTreeElement.prototype; | |
1539 | |
1540 WebInspector.ResourceGraph = function(resource) | |
1541 { | |
1542 this.resource = resource; | |
1543 | |
1544 this._graphElement = document.createElement("div"); | |
1545 this._graphElement.className = "resources-graph-side"; | |
1546 this._graphElement.addEventListener("mouseover", this.refreshLabelPositions.
bind(this), false); | |
1547 | |
1548 if (resource.cached) | |
1549 this._graphElement.addStyleClass("resource-cached"); | |
1550 | |
1551 this._barAreaElement = document.createElement("div"); | |
1552 this._barAreaElement.className = "resources-graph-bar-area hidden"; | |
1553 this._graphElement.appendChild(this._barAreaElement); | |
1554 | |
1555 this._barLeftElement = document.createElement("div"); | |
1556 this._barLeftElement.className = "resources-graph-bar waiting"; | |
1557 this._barAreaElement.appendChild(this._barLeftElement); | |
1558 | |
1559 this._barRightElement = document.createElement("div"); | |
1560 this._barRightElement.className = "resources-graph-bar"; | |
1561 this._barAreaElement.appendChild(this._barRightElement); | |
1562 | |
1563 this._labelLeftElement = document.createElement("div"); | |
1564 this._labelLeftElement.className = "resources-graph-label waiting"; | |
1565 this._barAreaElement.appendChild(this._labelLeftElement); | |
1566 | |
1567 this._labelRightElement = document.createElement("div"); | |
1568 this._labelRightElement.className = "resources-graph-label"; | |
1569 this._barAreaElement.appendChild(this._labelRightElement); | |
1570 | |
1571 this._graphElement.addStyleClass("resources-category-" + resource.category.n
ame); | |
1572 } | |
1573 | |
1574 WebInspector.ResourceGraph.prototype = { | |
1575 get graphElement() | |
1576 { | |
1577 return this._graphElement; | |
1578 }, | |
1579 | |
1580 refreshLabelPositions: function() | |
1581 { | |
1582 this._labelLeftElement.style.removeProperty("left"); | |
1583 this._labelLeftElement.style.removeProperty("right"); | |
1584 this._labelLeftElement.removeStyleClass("before"); | |
1585 this._labelLeftElement.removeStyleClass("hidden"); | |
1586 | |
1587 this._labelRightElement.style.removeProperty("left"); | |
1588 this._labelRightElement.style.removeProperty("right"); | |
1589 this._labelRightElement.removeStyleClass("after"); | |
1590 this._labelRightElement.removeStyleClass("hidden"); | |
1591 | |
1592 const labelPadding = 10; | |
1593 const rightBarWidth = (this._barRightElement.offsetWidth - labelPadding)
; | |
1594 const leftBarWidth = ((this._barLeftElement.offsetWidth - this._barRight
Element.offsetWidth) - labelPadding); | |
1595 | |
1596 var labelBefore = (this._labelLeftElement.offsetWidth > leftBarWidth); | |
1597 var labelAfter = (this._labelRightElement.offsetWidth > rightBarWidth); | |
1598 | |
1599 if (labelBefore) { | |
1600 if ((this._graphElement.offsetWidth * (this._percentages.start / 100
)) < (this._labelLeftElement.offsetWidth + 10)) | |
1601 this._labelLeftElement.addStyleClass("hidden"); | |
1602 this._labelLeftElement.style.setProperty("right", (100 - this._perce
ntages.start) + "%"); | |
1603 this._labelLeftElement.addStyleClass("before"); | |
1604 } else { | |
1605 this._labelLeftElement.style.setProperty("left", this._percentages.s
tart + "%"); | |
1606 this._labelLeftElement.style.setProperty("right", (100 - this._perce
ntages.middle) + "%"); | |
1607 } | |
1608 | |
1609 if (labelAfter) { | |
1610 if ((this._graphElement.offsetWidth * ((100 - this._percentages.end)
/ 100)) < (this._labelRightElement.offsetWidth + 10)) | |
1611 this._labelRightElement.addStyleClass("hidden"); | |
1612 this._labelRightElement.style.setProperty("left", this._percentages.
end + "%"); | |
1613 this._labelRightElement.addStyleClass("after"); | |
1614 } else { | |
1615 this._labelRightElement.style.setProperty("left", this._percentages.
middle + "%"); | |
1616 this._labelRightElement.style.setProperty("right", (100 - this._perc
entages.end) + "%"); | |
1617 } | |
1618 }, | |
1619 | |
1620 refresh: function(calculator) | |
1621 { | |
1622 var percentages = calculator.computeBarGraphPercentages(this.resource); | |
1623 var labels = calculator.computeBarGraphLabels(this.resource); | |
1624 | |
1625 this._percentages = percentages; | |
1626 | |
1627 this._barAreaElement.removeStyleClass("hidden"); | |
1628 | |
1629 if (!this._graphElement.hasStyleClass("resources-category-" + this.resou
rce.category.name)) { | |
1630 this._graphElement.removeMatchingStyleClasses("resources-category-\\
w+"); | |
1631 this._graphElement.addStyleClass("resources-category-" + this.resour
ce.category.name); | |
1632 } | |
1633 | |
1634 this._barLeftElement.style.setProperty("left", percentages.start + "%"); | |
1635 this._barLeftElement.style.setProperty("right", (100 - percentages.end)
+ "%"); | |
1636 | |
1637 this._barRightElement.style.setProperty("left", percentages.middle + "%"
); | |
1638 this._barRightElement.style.setProperty("right", (100 - percentages.end)
+ "%"); | |
1639 | |
1640 this._labelLeftElement.textContent = labels.left; | |
1641 this._labelRightElement.textContent = labels.right; | |
1642 | |
1643 var tooltip = (labels.tooltip || ""); | |
1644 this._barLeftElement.title = tooltip; | |
1645 this._labelLeftElement.title = tooltip; | |
1646 this._labelRightElement.title = tooltip; | |
1647 this._barRightElement.title = tooltip; | |
1648 } | |
1649 } | |
OLD | NEW |