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(identifier, url) 30{ 31 this.identifier = identifier; 32 this._url = url; 33 this._startTime = -1; 34 this._endTime = -1; 35 this._requestMethod = ""; 36 this._requestFormData = ""; 37 this._category = WebInspector.resourceCategories.other; 38} 39 40WebInspector.Resource.StatusText = { 41 100: "Continue", 42 101: "Switching Protocols", 43 102: "Processing (WebDav)", 44 200: "OK", 45 201: "Created", 46 202: "Accepted", 47 203: "Non-Authoritative Information", 48 204: "No Content", 49 205: "Reset Content", 50 206: "Partial Content", 51 207: "Multi-Status (WebDav)", 52 300: "Multiple Choices", 53 301: "Moved Permanently", 54 302: "Found", 55 303: "See Other", 56 304: "Not Modified", 57 305: "Use Proxy", 58 306: "Switch Proxy", 59 307: "Temporary", 60 400: "Bad Request", 61 401: "Unauthorized", 62 402: "Payment Required", 63 403: "Forbidden", 64 404: "Not Found", 65 405: "Method Not Allowed", 66 406: "Not Acceptable", 67 407: "Proxy Authentication Required", 68 408: "Request Timeout", 69 409: "Conflict", 70 410: "Gone", 71 411: "Length Required", 72 412: "Precondition Failed", 73 413: "Request Entity Too Large", 74 414: "Request-URI Too Long", 75 415: "Unsupported Media Type", 76 416: "Requested Range Not Satisfiable", 77 417: "Expectation Failed", 78 418: "I'm a teapot", 79 422: "Unprocessable Entity (WebDav)", 80 423: "Locked (WebDav)", 81 424: "Failed Dependency (WebDav)", 82 425: "Unordered Collection", 83 426: "Upgrade Required", 84 449: "Retry With", 85 500: "Internal Server Error", 86 501: "Not Implemented", 87 502: "Bad Gateway", 88 503: "Service Unavailable", 89 504: "Gateway Timeout", 90 505: "HTTP Version Not Supported", 91 506: "Variant Also Negotiates", 92 507: "Insufficient Storage (WebDav)", 93 509: "Bandwidth Limit Exceeded", 94 510: "Not Extended" 95}; 96 97// Keep these in sync with WebCore::InspectorResource::Type 98WebInspector.Resource.Type = { 99 Document: 0, 100 Stylesheet: 1, 101 Image: 2, 102 Font: 3, 103 Script: 4, 104 XHR: 5, 105 Media: 6, 106 Other: 7, 107 108 isTextType: function(type) 109 { 110 return (type === this.Document) || (type === this.Stylesheet) || (type === this.Script) || (type === this.XHR); 111 }, 112 113 toString: function(type) 114 { 115 switch (type) { 116 case this.Document: 117 return WebInspector.UIString("document"); 118 case this.Stylesheet: 119 return WebInspector.UIString("stylesheet"); 120 case this.Image: 121 return WebInspector.UIString("image"); 122 case this.Font: 123 return WebInspector.UIString("font"); 124 case this.Script: 125 return WebInspector.UIString("script"); 126 case this.XHR: 127 return WebInspector.UIString("XHR"); 128 case this.Other: 129 default: 130 return WebInspector.UIString("other"); 131 } 132 } 133} 134 135WebInspector.Resource.prototype = { 136 get url() 137 { 138 return this._url; 139 }, 140 141 set url(x) 142 { 143 if (this._url === x) 144 return; 145 146 var oldURL = this._url; 147 this._url = x; 148 149 // FIXME: We should make the WebInspector object listen for the "url changed" event. 150 // Then resourceURLChanged can be removed. 151 WebInspector.resourceURLChanged(this, oldURL); 152 153 this.dispatchEventToListeners("url changed"); 154 }, 155 156 get documentURL() 157 { 158 return this._documentURL; 159 }, 160 161 set documentURL(x) 162 { 163 if (this._documentURL === x) 164 return; 165 this._documentURL = x; 166 }, 167 168 get domain() 169 { 170 return this._domain; 171 }, 172 173 set domain(x) 174 { 175 if (this._domain === x) 176 return; 177 this._domain = x; 178 }, 179 180 get lastPathComponent() 181 { 182 return this._lastPathComponent; 183 }, 184 185 set lastPathComponent(x) 186 { 187 if (this._lastPathComponent === x) 188 return; 189 this._lastPathComponent = x; 190 this._lastPathComponentLowerCase = x ? x.toLowerCase() : null; 191 }, 192 193 get displayName() 194 { 195 var title = this.lastPathComponent; 196 if (!title) 197 title = this.displayDomain; 198 if (!title && this.url) 199 title = this.url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); 200 if (title === "/") 201 title = this.url; 202 return title; 203 }, 204 205 get displayDomain() 206 { 207 // WebInspector.Database calls this, so don't access more than this.domain. 208 if (this.domain && (!WebInspector.mainResource || (WebInspector.mainResource && this.domain !== WebInspector.mainResource.domain))) 209 return this.domain; 210 return ""; 211 }, 212 213 get startTime() 214 { 215 return this._startTime || -1; 216 }, 217 218 set startTime(x) 219 { 220 if (this._startTime === x) 221 return; 222 223 this._startTime = x; 224 225 if (WebInspector.panels.resources) 226 WebInspector.panels.resources.refreshResource(this); 227 }, 228 229 get responseReceivedTime() 230 { 231 return this._responseReceivedTime || -1; 232 }, 233 234 set responseReceivedTime(x) 235 { 236 if (this._responseReceivedTime === x) 237 return; 238 239 this._responseReceivedTime = x; 240 241 if (WebInspector.panels.resources) 242 WebInspector.panels.resources.refreshResource(this); 243 }, 244 245 get endTime() 246 { 247 return this._endTime || -1; 248 }, 249 250 set endTime(x) 251 { 252 if (this._endTime === x) 253 return; 254 255 this._endTime = x; 256 257 if (WebInspector.panels.resources) 258 WebInspector.panels.resources.refreshResource(this); 259 }, 260 261 get duration() 262 { 263 if (this._endTime === -1 || this._startTime === -1) 264 return -1; 265 return this._endTime - this._startTime; 266 }, 267 268 get latency() 269 { 270 if (this._responseReceivedTime === -1 || this._startTime === -1) 271 return -1; 272 return this._responseReceivedTime - this._startTime; 273 }, 274 275 get contentLength() 276 { 277 return this._contentLength || 0; 278 }, 279 280 set contentLength(x) 281 { 282 if (this._contentLength === x) 283 return; 284 285 this._contentLength = x; 286 287 if (WebInspector.panels.resources) 288 WebInspector.panels.resources.refreshResource(this); 289 }, 290 291 get expectedContentLength() 292 { 293 return this._expectedContentLength || 0; 294 }, 295 296 set expectedContentLength(x) 297 { 298 if (this._expectedContentLength === x) 299 return; 300 this._expectedContentLength = x; 301 }, 302 303 get finished() 304 { 305 return this._finished; 306 }, 307 308 set finished(x) 309 { 310 if (this._finished === x) 311 return; 312 313 this._finished = x; 314 315 if (x) { 316 this._checkWarnings(); 317 this.dispatchEventToListeners("finished"); 318 } 319 }, 320 321 get failed() 322 { 323 return this._failed; 324 }, 325 326 set failed(x) 327 { 328 this._failed = x; 329 }, 330 331 get category() 332 { 333 return this._category; 334 }, 335 336 set category(x) 337 { 338 if (this._category === x) 339 return; 340 341 var oldCategory = this._category; 342 if (oldCategory) 343 oldCategory.removeResource(this); 344 345 this._category = x; 346 347 if (this._category) 348 this._category.addResource(this); 349 350 if (WebInspector.panels.resources) { 351 WebInspector.panels.resources.refreshResource(this); 352 WebInspector.panels.resources.recreateViewForResourceIfNeeded(this); 353 } 354 }, 355 356 get mimeType() 357 { 358 return this._mimeType; 359 }, 360 361 set mimeType(x) 362 { 363 if (this._mimeType === x) 364 return; 365 366 this._mimeType = x; 367 }, 368 369 get type() 370 { 371 return this._type; 372 }, 373 374 set type(x) 375 { 376 if (this._type === x) 377 return; 378 379 this._type = x; 380 381 switch (x) { 382 case WebInspector.Resource.Type.Document: 383 this.category = WebInspector.resourceCategories.documents; 384 break; 385 case WebInspector.Resource.Type.Stylesheet: 386 this.category = WebInspector.resourceCategories.stylesheets; 387 break; 388 case WebInspector.Resource.Type.Script: 389 this.category = WebInspector.resourceCategories.scripts; 390 break; 391 case WebInspector.Resource.Type.Image: 392 this.category = WebInspector.resourceCategories.images; 393 break; 394 case WebInspector.Resource.Type.Font: 395 this.category = WebInspector.resourceCategories.fonts; 396 break; 397 case WebInspector.Resource.Type.XHR: 398 this.category = WebInspector.resourceCategories.xhr; 399 break; 400 case WebInspector.Resource.Type.Other: 401 default: 402 this.category = WebInspector.resourceCategories.other; 403 break; 404 } 405 }, 406 407 get requestHeaders() 408 { 409 if (this._requestHeaders === undefined) 410 this._requestHeaders = {}; 411 return this._requestHeaders; 412 }, 413 414 set requestHeaders(x) 415 { 416 if (this._requestHeaders === x) 417 return; 418 419 this._requestHeaders = x; 420 delete this._sortedRequestHeaders; 421 422 this.dispatchEventToListeners("requestHeaders changed"); 423 }, 424 425 get sortedRequestHeaders() 426 { 427 if (this._sortedRequestHeaders !== undefined) 428 return this._sortedRequestHeaders; 429 430 this._sortedRequestHeaders = []; 431 for (var key in this.requestHeaders) 432 this._sortedRequestHeaders.push({header: key, value: this.requestHeaders[key]}); 433 this._sortedRequestHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); 434 435 return this._sortedRequestHeaders; 436 }, 437 438 get responseHeaders() 439 { 440 if (this._responseHeaders === undefined) 441 this._responseHeaders = {}; 442 return this._responseHeaders; 443 }, 444 445 set responseHeaders(x) 446 { 447 if (this._responseHeaders === x) 448 return; 449 450 this._responseHeaders = x; 451 delete this._sortedResponseHeaders; 452 453 this.dispatchEventToListeners("responseHeaders changed"); 454 }, 455 456 get sortedResponseHeaders() 457 { 458 if (this._sortedResponseHeaders !== undefined) 459 return this._sortedResponseHeaders; 460 461 this._sortedResponseHeaders = []; 462 for (var key in this.responseHeaders) 463 this._sortedResponseHeaders.push({header: key, value: this.responseHeaders[key]}); 464 this._sortedResponseHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); 465 466 return this._sortedResponseHeaders; 467 }, 468 469 get scripts() 470 { 471 if (!("_scripts" in this)) 472 this._scripts = []; 473 return this._scripts; 474 }, 475 476 addScript: function(script) 477 { 478 if (!script) 479 return; 480 this.scripts.unshift(script); 481 script.resource = this; 482 }, 483 484 removeAllScripts: function() 485 { 486 if (!this._scripts) 487 return; 488 489 for (var i = 0; i < this._scripts.length; ++i) { 490 if (this._scripts[i].resource === this) 491 delete this._scripts[i].resource; 492 } 493 494 delete this._scripts; 495 }, 496 497 removeScript: function(script) 498 { 499 if (!script) 500 return; 501 502 if (script.resource === this) 503 delete script.resource; 504 505 if (!this._scripts) 506 return; 507 508 this._scripts.remove(script); 509 }, 510 511 get errors() 512 { 513 return this._errors || 0; 514 }, 515 516 set errors(x) 517 { 518 this._errors = x; 519 }, 520 521 get warnings() 522 { 523 return this._warnings || 0; 524 }, 525 526 set warnings(x) 527 { 528 this._warnings = x; 529 }, 530 531 _mimeTypeIsConsistentWithType: function() 532 { 533 if (typeof this.type === "undefined" 534 || this.type === WebInspector.Resource.Type.Other 535 || this.type === WebInspector.Resource.Type.XHR) 536 return true; 537 538 if (this.mimeType in WebInspector.MIMETypes) 539 return this.type in WebInspector.MIMETypes[this.mimeType]; 540 541 return false; 542 }, 543 544 _checkWarnings: function() 545 { 546 for (var warning in WebInspector.Warnings) 547 this._checkWarning(WebInspector.Warnings[warning]); 548 }, 549 550 _checkWarning: function(warning) 551 { 552 var msg; 553 switch (warning.id) { 554 case WebInspector.Warnings.IncorrectMIMEType.id: 555 if (!this._mimeTypeIsConsistentWithType()) 556 msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other, 557 WebInspector.ConsoleMessage.MessageType.Log, 558 WebInspector.ConsoleMessage.MessageLevel.Warning, -1, this.url, null, 1, 559 String.sprintf(WebInspector.Warnings.IncorrectMIMEType.message, 560 WebInspector.Resource.Type.toString(this.type), this.mimeType)); 561 break; 562 } 563 564 if (msg) 565 WebInspector.console.addMessage(msg); 566 } 567} 568 569WebInspector.Resource.prototype.__proto__ = WebInspector.Object.prototype; 570 571WebInspector.Resource.CompareByStartTime = function(a, b) 572{ 573 return a.startTime - b.startTime; 574} 575 576WebInspector.Resource.CompareByResponseReceivedTime = function(a, b) 577{ 578 var aVal = a.responseReceivedTime; 579 var bVal = b.responseReceivedTime; 580 if (aVal === -1 ^ bVal === -1) 581 return bVal - aVal; 582 return aVal - bVal; 583} 584 585WebInspector.Resource.CompareByEndTime = function(a, b) 586{ 587 var aVal = a.endTime; 588 var bVal = b.endTime; 589 if (aVal === -1 ^ bVal === -1) 590 return bVal - aVal; 591 return aVal - bVal; 592} 593 594WebInspector.Resource.CompareByDuration = function(a, b) 595{ 596 return a.duration - b.duration; 597} 598 599WebInspector.Resource.CompareByLatency = function(a, b) 600{ 601 return a.latency - b.latency; 602} 603 604WebInspector.Resource.CompareBySize = function(a, b) 605{ 606 return a.contentLength - b.contentLength; 607} 608 609WebInspector.Resource.StatusTextForCode = function(code) 610{ 611 return code ? code + " " + WebInspector.Resource.StatusText[code] : ""; 612} 613