1/* 2 * Copyright (C) 2007, 2008 Apple 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 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29WebInspector.Resource = function(requestHeaders, url, domain, path, lastPathComponent, identifier, mainResource, cached) 30{ 31 this.identifier = identifier; 32 33 this.startTime = -1; 34 this.endTime = -1; 35 this.mainResource = mainResource; 36 this.requestHeaders = requestHeaders; 37 this.url = url; 38 this.domain = domain; 39 this.path = path; 40 this.lastPathComponent = lastPathComponent; 41 this.cached = cached; 42 43 this.category = WebInspector.resourceCategories.other; 44} 45 46// Keep these in sync with WebCore::InspectorResource::Type 47WebInspector.Resource.Type = { 48 Document: 0, 49 Stylesheet: 1, 50 Image: 2, 51 Font: 3, 52 Script: 4, 53 XHR: 5, 54 Other: 6, 55 56 isTextType: function(type) 57 { 58 return (type === this.Document) || (type === this.Stylesheet) || (type === this.Script) || (type === this.XHR); 59 }, 60 61 toString: function(type) 62 { 63 switch (type) { 64 case this.Document: 65 return WebInspector.UIString("document"); 66 case this.Stylesheet: 67 return WebInspector.UIString("stylesheet"); 68 case this.Image: 69 return WebInspector.UIString("image"); 70 case this.Font: 71 return WebInspector.UIString("font"); 72 case this.Script: 73 return WebInspector.UIString("script"); 74 case this.XHR: 75 return WebInspector.UIString("XHR"); 76 case this.Other: 77 default: 78 return WebInspector.UIString("other"); 79 } 80 } 81} 82 83WebInspector.Resource.prototype = { 84 get url() 85 { 86 return this._url; 87 }, 88 89 set url(x) 90 { 91 if (this._url === x) 92 return; 93 94 var oldURL = this._url; 95 this._url = x; 96 97 // FIXME: We should make the WebInspector object listen for the "url changed" event. 98 // Then resourceURLChanged can be removed. 99 WebInspector.resourceURLChanged(this, oldURL); 100 101 this.dispatchEventToListeners("url changed"); 102 }, 103 104 get domain() 105 { 106 return this._domain; 107 }, 108 109 set domain(x) 110 { 111 if (this._domain === x) 112 return; 113 this._domain = x; 114 }, 115 116 get lastPathComponent() 117 { 118 return this._lastPathComponent; 119 }, 120 121 set lastPathComponent(x) 122 { 123 if (this._lastPathComponent === x) 124 return; 125 this._lastPathComponent = x; 126 this._lastPathComponentLowerCase = x ? x.toLowerCase() : null; 127 }, 128 129 get displayName() 130 { 131 var title = this.lastPathComponent; 132 if (!title) 133 title = this.displayDomain; 134 if (!title && this.url) 135 title = this.url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); 136 if (title === "/") 137 title = this.url; 138 return title; 139 }, 140 141 get displayDomain() 142 { 143 // WebInspector.Database calls this, so don't access more than this.domain. 144 if (this.domain && (!WebInspector.mainResource || (WebInspector.mainResource && this.domain !== WebInspector.mainResource.domain))) 145 return this.domain; 146 return ""; 147 }, 148 149 get startTime() 150 { 151 return this._startTime || -1; 152 }, 153 154 set startTime(x) 155 { 156 if (this._startTime === x) 157 return; 158 159 this._startTime = x; 160 161 if (WebInspector.panels.resources) 162 WebInspector.panels.resources.refreshResource(this); 163 }, 164 165 get responseReceivedTime() 166 { 167 return this._responseReceivedTime || -1; 168 }, 169 170 set responseReceivedTime(x) 171 { 172 if (this._responseReceivedTime === x) 173 return; 174 175 this._responseReceivedTime = x; 176 177 if (WebInspector.panels.resources) 178 WebInspector.panels.resources.refreshResource(this); 179 }, 180 181 get endTime() 182 { 183 return this._endTime || -1; 184 }, 185 186 set endTime(x) 187 { 188 if (this._endTime === x) 189 return; 190 191 this._endTime = x; 192 193 if (WebInspector.panels.resources) 194 WebInspector.panels.resources.refreshResource(this); 195 }, 196 197 get duration() 198 { 199 if (this._endTime === -1 || this._startTime === -1) 200 return -1; 201 return this._endTime - this._startTime; 202 }, 203 204 get latency() 205 { 206 if (this._responseReceivedTime === -1 || this._startTime === -1) 207 return -1; 208 return this._responseReceivedTime - this._startTime; 209 }, 210 211 get contentLength() 212 { 213 return this._contentLength || 0; 214 }, 215 216 set contentLength(x) 217 { 218 if (this._contentLength === x) 219 return; 220 221 this._contentLength = x; 222 223 if (WebInspector.panels.resources) 224 WebInspector.panels.resources.refreshResource(this); 225 }, 226 227 get expectedContentLength() 228 { 229 return this._expectedContentLength || 0; 230 }, 231 232 set expectedContentLength(x) 233 { 234 if (this._expectedContentLength === x) 235 return; 236 this._expectedContentLength = x; 237 }, 238 239 get finished() 240 { 241 return this._finished; 242 }, 243 244 set finished(x) 245 { 246 if (this._finished === x) 247 return; 248 249 this._finished = x; 250 251 if (x) { 252 this._checkTips(); 253 this._checkWarnings(); 254 this.dispatchEventToListeners("finished"); 255 } 256 }, 257 258 get failed() 259 { 260 return this._failed; 261 }, 262 263 set failed(x) 264 { 265 this._failed = x; 266 }, 267 268 get category() 269 { 270 return this._category; 271 }, 272 273 set category(x) 274 { 275 if (this._category === x) 276 return; 277 278 var oldCategory = this._category; 279 if (oldCategory) 280 oldCategory.removeResource(this); 281 282 this._category = x; 283 284 if (this._category) 285 this._category.addResource(this); 286 287 if (WebInspector.panels.resources) { 288 WebInspector.panels.resources.refreshResource(this); 289 WebInspector.panels.resources.recreateViewForResourceIfNeeded(this); 290 } 291 }, 292 293 get mimeType() 294 { 295 return this._mimeType; 296 }, 297 298 set mimeType(x) 299 { 300 if (this._mimeType === x) 301 return; 302 303 this._mimeType = x; 304 }, 305 306 get type() 307 { 308 return this._type; 309 }, 310 311 set type(x) 312 { 313 if (this._type === x) 314 return; 315 316 this._type = x; 317 318 switch (x) { 319 case WebInspector.Resource.Type.Document: 320 this.category = WebInspector.resourceCategories.documents; 321 break; 322 case WebInspector.Resource.Type.Stylesheet: 323 this.category = WebInspector.resourceCategories.stylesheets; 324 break; 325 case WebInspector.Resource.Type.Script: 326 this.category = WebInspector.resourceCategories.scripts; 327 break; 328 case WebInspector.Resource.Type.Image: 329 this.category = WebInspector.resourceCategories.images; 330 break; 331 case WebInspector.Resource.Type.Font: 332 this.category = WebInspector.resourceCategories.fonts; 333 break; 334 case WebInspector.Resource.Type.XHR: 335 this.category = WebInspector.resourceCategories.xhr; 336 break; 337 case WebInspector.Resource.Type.Other: 338 default: 339 this.category = WebInspector.resourceCategories.other; 340 break; 341 } 342 }, 343 344 get requestHeaders() 345 { 346 if (this._requestHeaders === undefined) 347 this._requestHeaders = {}; 348 return this._requestHeaders; 349 }, 350 351 set requestHeaders(x) 352 { 353 if (this._requestHeaders === x) 354 return; 355 356 this._requestHeaders = x; 357 delete this._sortedRequestHeaders; 358 359 this.dispatchEventToListeners("requestHeaders changed"); 360 }, 361 362 get sortedRequestHeaders() 363 { 364 if (this._sortedRequestHeaders !== undefined) 365 return this._sortedRequestHeaders; 366 367 this._sortedRequestHeaders = []; 368 for (var key in this.requestHeaders) 369 this._sortedRequestHeaders.push({header: key, value: this.requestHeaders[key]}); 370 this._sortedRequestHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); 371 372 return this._sortedRequestHeaders; 373 }, 374 375 get responseHeaders() 376 { 377 if (this._responseHeaders === undefined) 378 this._responseHeaders = {}; 379 return this._responseHeaders; 380 }, 381 382 set responseHeaders(x) 383 { 384 if (this._responseHeaders === x) 385 return; 386 387 this._responseHeaders = x; 388 delete this._sortedResponseHeaders; 389 390 this.dispatchEventToListeners("responseHeaders changed"); 391 }, 392 393 get sortedResponseHeaders() 394 { 395 if (this._sortedResponseHeaders !== undefined) 396 return this._sortedResponseHeaders; 397 398 this._sortedResponseHeaders = []; 399 for (var key in this.responseHeaders) 400 this._sortedResponseHeaders.push({header: key, value: this.responseHeaders[key]}); 401 this._sortedResponseHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); 402 403 return this._sortedResponseHeaders; 404 }, 405 406 get scripts() 407 { 408 if (!("_scripts" in this)) 409 this._scripts = []; 410 return this._scripts; 411 }, 412 413 addScript: function(script) 414 { 415 if (!script) 416 return; 417 this.scripts.unshift(script); 418 script.resource = this; 419 }, 420 421 removeAllScripts: function() 422 { 423 if (!this._scripts) 424 return; 425 426 for (var i = 0; i < this._scripts.length; ++i) { 427 if (this._scripts[i].resource === this) 428 delete this._scripts[i].resource; 429 } 430 431 delete this._scripts; 432 }, 433 434 removeScript: function(script) 435 { 436 if (!script) 437 return; 438 439 if (script.resource === this) 440 delete script.resource; 441 442 if (!this._scripts) 443 return; 444 445 this._scripts.remove(script); 446 }, 447 448 get errors() 449 { 450 return this._errors || 0; 451 }, 452 453 set errors(x) 454 { 455 this._errors = x; 456 }, 457 458 get warnings() 459 { 460 return this._warnings || 0; 461 }, 462 463 set warnings(x) 464 { 465 this._warnings = x; 466 }, 467 468 get tips() 469 { 470 if (!("_tips" in this)) 471 this._tips = {}; 472 return this._tips; 473 }, 474 475 _addTip: function(tip) 476 { 477 if (tip.id in this.tips) 478 return; 479 480 this.tips[tip.id] = tip; 481 482 // FIXME: Re-enable this code once we have a scope bar in the Console. 483 // Otherwise, we flood the Console with too many tips. 484 /* 485 var msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other, 486 WebInspector.ConsoleMessage.MessageType.Log, WebInspector.ConsoleMessage.MessageLevel.Tip, 487 -1, this.url, null, 1, tip.message); 488 WebInspector.console.addMessage(msg); 489 */ 490 }, 491 492 _checkTips: function() 493 { 494 for (var tip in WebInspector.Tips) 495 this._checkTip(WebInspector.Tips[tip]); 496 }, 497 498 _checkTip: function(tip) 499 { 500 var addTip = false; 501 switch (tip.id) { 502 case WebInspector.Tips.ResourceNotCompressed.id: 503 addTip = this._shouldCompress(); 504 break; 505 } 506 507 if (addTip) 508 this._addTip(tip); 509 }, 510 511 _shouldCompress: function() 512 { 513 return WebInspector.Resource.Type.isTextType(this.type) 514 && this.domain 515 && !("Content-Encoding" in this.responseHeaders) 516 && this.contentLength !== undefined 517 && this.contentLength >= 512; 518 }, 519 520 _mimeTypeIsConsistentWithType: function() 521 { 522 if (typeof this.type === "undefined" 523 || this.type === WebInspector.Resource.Type.Other 524 || this.type === WebInspector.Resource.Type.XHR) 525 return true; 526 527 if (this.mimeType in WebInspector.MIMETypes) 528 return this.type in WebInspector.MIMETypes[this.mimeType]; 529 530 return true; 531 }, 532 533 _checkWarnings: function() 534 { 535 for (var warning in WebInspector.Warnings) 536 this._checkWarning(WebInspector.Warnings[warning]); 537 }, 538 539 _checkWarning: function(warning) 540 { 541 var addWarning = false; 542 var msg; 543 switch (warning.id) { 544 case WebInspector.Warnings.IncorrectMIMEType.id: 545 if (!this._mimeTypeIsConsistentWithType()) 546 msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other, 547 WebInspector.ConsoleMessage.MessageType.Log, 548 WebInspector.ConsoleMessage.MessageLevel.Warning, -1, this.url, null, 1, 549 String.sprintf(WebInspector.Warnings.IncorrectMIMEType.message, 550 WebInspector.Resource.Type.toString(this.type), this.mimeType)); 551 break; 552 } 553 554 if (msg) 555 WebInspector.console.addMessage(msg); 556 } 557} 558 559WebInspector.Resource.prototype.__proto__ = WebInspector.Object.prototype; 560 561WebInspector.Resource.CompareByStartTime = function(a, b) 562{ 563 if (a.startTime < b.startTime) 564 return -1; 565 if (a.startTime > b.startTime) 566 return 1; 567 return 0; 568} 569 570WebInspector.Resource.CompareByResponseReceivedTime = function(a, b) 571{ 572 if (a.responseReceivedTime === -1 && b.responseReceivedTime !== -1) 573 return 1; 574 if (a.responseReceivedTime !== -1 && b.responseReceivedTime === -1) 575 return -1; 576 if (a.responseReceivedTime < b.responseReceivedTime) 577 return -1; 578 if (a.responseReceivedTime > b.responseReceivedTime) 579 return 1; 580 return 0; 581} 582 583WebInspector.Resource.CompareByEndTime = function(a, b) 584{ 585 if (a.endTime === -1 && b.endTime !== -1) 586 return 1; 587 if (a.endTime !== -1 && b.endTime === -1) 588 return -1; 589 if (a.endTime < b.endTime) 590 return -1; 591 if (a.endTime > b.endTime) 592 return 1; 593 return 0; 594} 595 596WebInspector.Resource.CompareByDuration = function(a, b) 597{ 598 if (a.duration < b.duration) 599 return -1; 600 if (a.duration > b.duration) 601 return 1; 602 return 0; 603} 604 605WebInspector.Resource.CompareByLatency = function(a, b) 606{ 607 if (a.latency < b.latency) 608 return -1; 609 if (a.latency > b.latency) 610 return 1; 611 return 0; 612} 613 614WebInspector.Resource.CompareBySize = function(a, b) 615{ 616 if (a.contentLength < b.contentLength) 617 return -1; 618 if (a.contentLength > b.contentLength) 619 return 1; 620 return 0; 621} 622