• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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