Web Inspector: Uncaught exception: TypeError: this._initiatorSourceCodeLocation.sourc...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Models / Resource.js
1 /*
2  * Copyright (C) 2013 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.Resource = class Resource extends WebInspector.SourceCode
27 {
28     constructor(url, mimeType, type, loaderIdentifier, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
29     {
30         super();
31
32         console.assert(url);
33
34         if (type in WebInspector.Resource.Type)
35             type = WebInspector.Resource.Type[type];
36
37         this._url = url;
38         this._urlComponents = null;
39         this._mimeType = mimeType;
40         this._mimeTypeComponents = null;
41         this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
42         this._loaderIdentifier = loaderIdentifier || null;
43         this._requestIdentifier = requestIdentifier || null;
44         this._requestMethod = requestMethod || null;
45         this._requestData = requestData || null;
46         this._requestHeaders = requestHeaders || {};
47         this._responseHeaders = {};
48         this._parentFrame = null;
49         this._initiatorSourceCodeLocation = initiatorSourceCodeLocation || null;
50         this._initiatedResources = [];
51         this._originalRequestWillBeSentTimestamp = originalRequestWillBeSentTimestamp || null;
52         this._requestSentTimestamp = requestSentTimestamp || NaN;
53         this._responseReceivedTimestamp = NaN;
54         this._lastRedirectReceivedTimestamp = NaN;
55         this._lastDataReceivedTimestamp = NaN;
56         this._finishedOrFailedTimestamp = NaN;
57         this._finishThenRequestContentPromise = null;
58         this._size = NaN;
59         this._transferSize = NaN;
60         this._cached = false;
61
62         if (this._initiatorSourceCodeLocation && this._initiatorSourceCodeLocation.sourceCode instanceof WebInspector.Resource)
63             this._initiatorSourceCodeLocation.sourceCode.addInitiatedResource(this);
64     }
65
66     // Static
67
68     static typeFromMIMEType(mimeType)
69     {
70         if (!mimeType)
71             return WebInspector.Resource.Type.Other;
72
73         mimeType = parseMIMEType(mimeType).type;
74
75         if (mimeType in WebInspector.Resource._mimeTypeMap)
76             return WebInspector.Resource._mimeTypeMap[mimeType];
77
78         if (mimeType.startsWith("image/"))
79             return WebInspector.Resource.Type.Image;
80
81         if (mimeType.startsWith("font/"))
82             return WebInspector.Resource.Type.Font;
83
84         return WebInspector.Resource.Type.Other;
85     }
86
87     static displayNameForType(type, plural)
88     {
89         switch(type) {
90         case WebInspector.Resource.Type.Document:
91             if (plural)
92                 return WebInspector.UIString("Documents");
93             return WebInspector.UIString("Document");
94         case WebInspector.Resource.Type.Stylesheet:
95             if (plural)
96                 return WebInspector.UIString("Stylesheets");
97             return WebInspector.UIString("Stylesheet");
98         case WebInspector.Resource.Type.Image:
99             if (plural)
100                 return WebInspector.UIString("Images");
101             return WebInspector.UIString("Image");
102         case WebInspector.Resource.Type.Font:
103             if (plural)
104                 return WebInspector.UIString("Fonts");
105             return WebInspector.UIString("Font");
106         case WebInspector.Resource.Type.Script:
107             if (plural)
108                 return WebInspector.UIString("Scripts");
109             return WebInspector.UIString("Script");
110         case WebInspector.Resource.Type.XHR:
111             if (plural)
112                 return WebInspector.UIString("XHRs");
113             return WebInspector.UIString("XHR");
114         case WebInspector.Resource.Type.WebSocket:
115             if (plural)
116                 return WebInspector.UIString("Sockets");
117             return WebInspector.UIString("Socket");
118         case WebInspector.Resource.Type.Other:
119             return WebInspector.UIString("Other");
120         default:
121             console.error("Unknown resource type: ", type);
122             return null;
123         }
124     }
125
126     // Public
127
128     get url()
129     {
130         return this._url;
131     }
132
133     get urlComponents()
134     {
135         if (!this._urlComponents)
136             this._urlComponents = parseURL(this._url);
137         return this._urlComponents;
138     }
139
140     get displayName()
141     {
142         return WebInspector.displayNameForURL(this._url, this.urlComponents);
143     }
144
145     get displayURL()
146     {
147         const isMultiLine = true;
148         const dataURIMaxSize = 64;
149         return WebInspector.truncateURL(this._url, isMultiLine, dataURIMaxSize);
150     }
151
152     get initiatorSourceCodeLocation()
153     {
154         return this._initiatorSourceCodeLocation;
155     }
156
157     get initiatedResources()
158     {
159         return this._initiatedResources;
160     }
161
162     get originalRequestWillBeSentTimestamp()
163     {
164         return this._originalRequestWillBeSentTimestamp;
165     }
166
167     get type()
168     {
169         return this._type;
170     }
171
172     get mimeType()
173     {
174         return this._mimeType;
175     }
176
177     get mimeTypeComponents()
178     {
179         if (!this._mimeTypeComponents)
180             this._mimeTypeComponents = parseMIMEType(this._mimeType);
181         return this._mimeTypeComponents;
182     }
183
184     get syntheticMIMEType()
185     {
186         // Resources are often transferred with a MIME-type that doesn't match the purpose the
187         // resource was loaded for, which is what WebInspector.Resource.Type represents.
188         // This getter generates a MIME-type, if needed, that matches the resource type.
189
190         // If the type matches the Resource.Type of the MIME-type, then return the actual MIME-type.
191         if (this._type === WebInspector.Resource.typeFromMIMEType(this._mimeType))
192             return this._mimeType;
193
194         // Return the default MIME-types for the Resource.Type, since the current MIME-type
195         // does not match what is expected for the Resource.Type.
196         switch (this._type) {
197         case WebInspector.Resource.Type.Document:
198             return "text/html";
199         case WebInspector.Resource.Type.Stylesheet:
200             return "text/css";
201         case WebInspector.Resource.Type.Script:
202             return "text/javascript";
203         }
204
205         // Return the actual MIME-type since we don't have a better synthesized one to return.
206         return this._mimeType;
207     }
208
209     createObjectURL()
210     {
211         // If content is not available, fallback to using original URL.
212         // The client may try to revoke it, but nothing will happen.
213         if (!this.content)
214             return this._url;
215
216         var content = this.content;
217         console.assert(content instanceof Blob, content);
218
219         return URL.createObjectURL(content);
220     }
221
222     isMainResource()
223     {
224         return this._parentFrame ? this._parentFrame.mainResource === this : false;
225     }
226
227     addInitiatedResource(resource)
228     {
229         if (!(resource instanceof WebInspector.Resource))
230             return;
231
232         this._initiatedResources.push(resource);
233     }
234
235     get parentFrame()
236     {
237         return this._parentFrame;
238     }
239
240     get loaderIdentifier()
241     {
242         return this._loaderIdentifier;
243     }
244
245     get requestIdentifier()
246     {
247         return this._requestIdentifier;
248     }
249
250     get finished()
251     {
252         return this._finished;
253     }
254
255     get failed()
256     {
257         return this._failed;
258     }
259
260     get canceled()
261     {
262         return this._canceled;
263     }
264
265     get requestMethod()
266     {
267         return this._requestMethod;
268     }
269
270     get requestData()
271     {
272         return this._requestData;
273     }
274
275     get requestDataContentType()
276     {
277         return this._requestHeaders.valueForCaseInsensitiveKey("Content-Type") || null;
278     }
279
280     get requestHeaders()
281     {
282         return this._requestHeaders;
283     }
284
285     get responseHeaders()
286     {
287         return this._responseHeaders;
288     }
289
290     get requestSentTimestamp()
291     {
292         return this._requestSentTimestamp;
293     }
294
295     get lastRedirectReceivedTimestamp()
296     {
297         return this._lastRedirectReceivedTimestamp;
298     }
299
300     get responseReceivedTimestamp()
301     {
302         return this._responseReceivedTimestamp;
303     }
304
305     get lastDataReceivedTimestamp()
306     {
307         return this._lastDataReceivedTimestamp;
308     }
309
310     get finishedOrFailedTimestamp()
311     {
312         return this._finishedOrFailedTimestamp;
313     }
314
315     get firstTimestamp()
316     {
317         return this.requestSentTimestamp || this.lastRedirectReceivedTimestamp || this.responseReceivedTimestamp || this.lastDataReceivedTimestamp || this.finishedOrFailedTimestamp;
318     }
319
320     get lastTimestamp()
321     {
322         return this.finishedOrFailedTimestamp || this.lastDataReceivedTimestamp || this.responseReceivedTimestamp || this.lastRedirectReceivedTimestamp || this.requestSentTimestamp;
323     }
324
325     get duration()
326     {
327         return this._finishedOrFailedTimestamp - this._requestSentTimestamp;
328     }
329
330     get latency()
331     {
332         return this._responseReceivedTimestamp - this._requestSentTimestamp;
333     }
334
335     get receiveDuration()
336     {
337         return this._finishedOrFailedTimestamp - this._responseReceivedTimestamp;
338     }
339
340     get cached()
341     {
342         return this._cached;
343     }
344
345     get statusCode()
346     {
347         return this._statusCode;
348     }
349
350     get statusText()
351     {
352         return this._statusText;
353     }
354
355     get size()
356     {
357         return this._size;
358     }
359
360     get encodedSize()
361     {
362         if (!isNaN(this._transferSize))
363             return this._transferSize;
364
365         // If we did not receive actual transfer size from network
366         // stack, we prefer using Content-Length over resourceSize as
367         // resourceSize may differ from actual transfer size if platform's
368         // network stack performed decoding (e.g. gzip decompression).
369         // The Content-Length, though, is expected to come from raw
370         // response headers and will reflect actual transfer length.
371         // This won't work for chunked content encoding, so fall back to
372         // resourceSize when we don't have Content-Length. This still won't
373         // work for chunks with non-trivial encodings. We need a way to
374         // get actual transfer size from the network stack.
375
376         return Number(this._responseHeaders.valueForCaseInsensitiveKey("Content-Length") || this._size);
377     }
378
379     get transferSize()
380     {
381         if (this.statusCode === 304) // Not modified
382             return this._responseHeadersSize;
383
384         if (this._cached)
385             return 0;
386
387         return this._responseHeadersSize + this.encodedSize;
388     }
389
390     get compressed()
391     {
392         var contentEncoding = this._responseHeaders.valueForCaseInsensitiveKey("Content-Encoding");
393         return contentEncoding && /\b(?:gzip|deflate)\b/.test(contentEncoding);
394     }
395
396     get scripts()
397     {
398         return this._scripts || [];
399     }
400
401     scriptForLocation(sourceCodeLocation)
402     {
403         console.assert(!(this instanceof WebInspector.SourceMapResource));
404         console.assert(sourceCodeLocation.sourceCode === this, "SourceCodeLocation must be in this Resource");
405         if (sourceCodeLocation.sourceCode !== this)
406             return null;
407
408         var lineNumber = sourceCodeLocation.lineNumber;
409         var columnNumber = sourceCodeLocation.columnNumber;
410         for (var i = 0; i < this._scripts.length; ++i) {
411             var script = this._scripts[i];
412             if (script.range.startLine <= lineNumber && script.range.endLine >= lineNumber) {
413                 if (script.range.startLine === lineNumber && columnNumber < script.range.startColumn)
414                     continue;
415                 if (script.range.endLine === lineNumber && columnNumber > script.range.endColumn)
416                     continue;
417                 return script;
418             }
419         }
420
421         return null;
422     }
423
424     updateForRedirectResponse(url, requestHeaders, elapsedTime)
425     {
426         console.assert(!this._finished);
427         console.assert(!this._failed);
428         console.assert(!this._canceled);
429
430         var oldURL = this._url;
431
432         this._url = url;
433         this._requestHeaders = requestHeaders || {};
434         this._lastRedirectReceivedTimestamp = elapsedTime || NaN;
435
436         if (oldURL !== url) {
437             // Delete the URL components so the URL is re-parsed the next time it is requested.
438             this._urlComponents = null;
439
440             this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
441         }
442
443         this.dispatchEventToListeners(WebInspector.Resource.Event.RequestHeadersDidChange);
444         this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
445     }
446
447     updateForResponse(url, mimeType, type, responseHeaders, statusCode, statusText, elapsedTime)
448     {
449         console.assert(!this._finished);
450         console.assert(!this._failed);
451         console.assert(!this._canceled);
452
453         var oldURL = this._url;
454         var oldMIMEType = this._mimeType;
455         var oldType = this._type;
456
457         if (type in WebInspector.Resource.Type)
458             type = WebInspector.Resource.Type[type];
459
460         this._url = url;
461         this._mimeType = mimeType;
462         this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
463         this._statusCode = statusCode;
464         this._statusText = statusText;
465         this._responseHeaders = responseHeaders || {};
466         this._responseReceivedTimestamp = elapsedTime || NaN;
467
468         this._responseHeadersSize = String(this._statusCode).length + this._statusText.length + 12; // Extra length is for "HTTP/1.1 ", " ", and "\r\n".
469         for (var name in this._responseHeaders)
470             this._responseHeadersSize += name.length + this._responseHeaders[name].length + 4; // Extra length is for ": ", and "\r\n".
471
472         if (statusCode === 304 && !this._cached)
473             this.markAsCached();
474
475         if (oldURL !== url) {
476             // Delete the URL components so the URL is re-parsed the next time it is requested.
477             this._urlComponents = null;
478
479             this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
480         }
481
482         if (oldMIMEType !== mimeType) {
483             // Delete the MIME-type components so the MIME-type is re-parsed the next time it is requested.
484             this._mimeTypeComponents = null;
485
486             this.dispatchEventToListeners(WebInspector.Resource.Event.MIMETypeDidChange, {oldMIMEType});
487         }
488
489         if (oldType !== type)
490             this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
491
492         console.assert(isNaN(this._size));
493         console.assert(isNaN(this._transferSize));
494
495         // The transferSize becomes 0 when status is 304 or Content-Length is available, so
496         // notify listeners of that change.
497         if (statusCode === 304 || this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
498             this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
499
500         this.dispatchEventToListeners(WebInspector.Resource.Event.ResponseReceived);
501         this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
502     }
503
504     canRequestContent()
505     {
506         return this._finished;
507     }
508
509     requestContentFromBackend()
510     {
511         // If we have the requestIdentifier we can get the actual response for this specific resource.
512         // Otherwise the content will be cached resource data, which might not exist anymore.
513         if (this._requestIdentifier)
514             return NetworkAgent.getResponseBody(this._requestIdentifier);
515
516         // There is no request identifier or frame to request content from.
517         if (this._parentFrame)
518             return PageAgent.getResourceContent(this._parentFrame.id, this._url);
519
520         return Promise.reject(new Error("Content request failed."));
521     }
522
523     increaseSize(dataLength, elapsedTime)
524     {
525         console.assert(dataLength >= 0);
526
527         if (isNaN(this._size))
528             this._size = 0;
529
530         var previousSize = this._size;
531
532         this._size += dataLength;
533
534         this._lastDataReceivedTimestamp = elapsedTime || NaN;
535
536         this.dispatchEventToListeners(WebInspector.Resource.Event.SizeDidChange, {previousSize});
537
538         // The transferSize is based off of size when status is not 304 or Content-Length is missing.
539         if (isNaN(this._transferSize) && this._statusCode !== 304 && !this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
540             this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
541     }
542
543     increaseTransferSize(encodedDataLength)
544     {
545         console.assert(encodedDataLength >= 0);
546
547         if (isNaN(this._transferSize))
548             this._transferSize = 0;
549         this._transferSize += encodedDataLength;
550
551         this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
552     }
553
554     markAsCached()
555     {
556         this._cached = true;
557
558         this.dispatchEventToListeners(WebInspector.Resource.Event.CacheStatusDidChange);
559
560         // The transferSize is starts returning 0 when cached is true, unless status is 304.
561         if (this._statusCode !== 304)
562             this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
563     }
564
565     markAsFinished(elapsedTime)
566     {
567         console.assert(!this._failed);
568         console.assert(!this._canceled);
569
570         this._finished = true;
571         this._finishedOrFailedTimestamp = elapsedTime || NaN;
572
573         if (this._finishThenRequestContentPromise)
574             this._finishThenRequestContentPromise = null;
575
576         this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFinish);
577         this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
578     }
579
580     markAsFailed(canceled, elapsedTime)
581     {
582         console.assert(!this._finished);
583
584         this._failed = true;
585         this._canceled = canceled;
586         this._finishedOrFailedTimestamp = elapsedTime || NaN;
587
588         this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFail);
589         this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
590     }
591
592     revertMarkAsFinished()
593     {
594         console.assert(!this._failed);
595         console.assert(!this._canceled);
596         console.assert(this._finished);
597
598         this._finished = false;
599         this._finishedOrFailedTimestamp = NaN;
600     }
601
602     getImageSize(callback)
603     {
604         // Throw an error in the case this resource is not an image.
605         if (this.type !== WebInspector.Resource.Type.Image)
606             throw "Resource is not an image.";
607
608         // See if we've already computed and cached the image size,
609         // in which case we can provide them directly.
610         if (this._imageSize) {
611             callback(this._imageSize);
612             return;
613         }
614
615         var objectURL = null;
616
617         // Event handler for the image "load" event.
618         function imageDidLoad()
619         {
620             URL.revokeObjectURL(objectURL);
621
622             // Cache the image metrics.
623             this._imageSize = {
624                 width: image.width,
625                 height: image.height
626             };
627
628             callback(this._imageSize);
629         }
630
631         // Create an <img> element that we'll use to load the image resource
632         // so that we can query its intrinsic size.
633         var image = new Image;
634         image.addEventListener("load", imageDidLoad.bind(this), false);
635
636         // Set the image source using an object URL once we've obtained its data.
637         this.requestContent().then(function(content) {
638             objectURL = image.src = content.sourceCode.createObjectURL();
639         });
640     }
641
642     requestContent()
643     {
644         if (this._finished)
645             return super.requestContent();
646
647         if (this._failed)
648             return Promise.resolve({error: WebInspector.UIString("An error occurred trying to load the resource.")});
649
650         if (!this._finishThenRequestContentPromise) {
651             this._finishThenRequestContentPromise = new Promise(function (resolve, reject) {
652                 this.addEventListener(WebInspector.Resource.Event.LoadingDidFinish, resolve);
653                 this.addEventListener(WebInspector.Resource.Event.LoadingDidFail, reject);
654             }.bind(this)).then(WebInspector.SourceCode.prototype.requestContent.bind(this));
655         }
656
657         return this._finishThenRequestContentPromise;
658     }
659
660     associateWithScript(script)
661     {
662         if (!this._scripts)
663             this._scripts = [];
664
665         this._scripts.push(script);
666
667         if (this._type === WebInspector.Resource.Type.Other) {
668             var oldType = this._type;
669             this._type = WebInspector.Resource.Type.Script;
670             this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
671         }
672     }
673
674     saveIdentityToCookie(cookie)
675     {
676         cookie[WebInspector.Resource.URLCookieKey] = this.url.hash;
677         cookie[WebInspector.Resource.MainResourceCookieKey] = this.isMainResource();
678     }
679 };
680
681 WebInspector.Resource.TypeIdentifier = "resource";
682 WebInspector.Resource.URLCookieKey = "resource-url";
683 WebInspector.Resource.MainResourceCookieKey = "resource-is-main-resource";
684
685 WebInspector.Resource.Event = {
686     URLDidChange: "resource-url-did-change",
687     MIMETypeDidChange: "resource-mime-type-did-change",
688     TypeDidChange: "resource-type-did-change",
689     RequestHeadersDidChange: "resource-request-headers-did-change",
690     ResponseReceived: "resource-response-received",
691     LoadingDidFinish: "resource-loading-did-finish",
692     LoadingDidFail: "resource-loading-did-fail",
693     TimestampsDidChange: "resource-timestamps-did-change",
694     SizeDidChange: "resource-size-did-change",
695     TransferSizeDidChange: "resource-transfer-size-did-change",
696     CacheStatusDidChange: "resource-cached-did-change"
697 };
698
699 // Keep these in sync with the "ResourceType" enum defined by the "Page" domain.
700 WebInspector.Resource.Type = {
701     Document: "resource-type-document",
702     Stylesheet: "resource-type-stylesheet",
703     Image: "resource-type-image",
704     Font: "resource-type-font",
705     Script: "resource-type-script",
706     XHR: "resource-type-xhr",
707     WebSocket: "resource-type-websocket",
708     Other: "resource-type-other"
709 };
710
711 // This MIME Type map is private, use WebInspector.Resource.typeFromMIMEType().
712 WebInspector.Resource._mimeTypeMap = {
713     "text/html": WebInspector.Resource.Type.Document,
714     "text/xml": WebInspector.Resource.Type.Document,
715     "text/plain": WebInspector.Resource.Type.Document,
716     "application/xhtml+xml": WebInspector.Resource.Type.Document,
717     "image/svg+xml": WebInspector.Resource.Type.Document,
718
719     "text/css": WebInspector.Resource.Type.Stylesheet,
720     "text/xsl": WebInspector.Resource.Type.Stylesheet,
721     "text/x-less": WebInspector.Resource.Type.Stylesheet,
722     "text/x-sass": WebInspector.Resource.Type.Stylesheet,
723     "text/x-scss": WebInspector.Resource.Type.Stylesheet,
724
725     "application/pdf": WebInspector.Resource.Type.Image,
726
727     "application/x-font-type1": WebInspector.Resource.Type.Font,
728     "application/x-font-ttf": WebInspector.Resource.Type.Font,
729     "application/x-font-woff": WebInspector.Resource.Type.Font,
730     "application/x-truetype-font": WebInspector.Resource.Type.Font,
731
732     "text/javascript": WebInspector.Resource.Type.Script,
733     "text/ecmascript": WebInspector.Resource.Type.Script,
734     "application/javascript": WebInspector.Resource.Type.Script,
735     "application/ecmascript": WebInspector.Resource.Type.Script,
736     "application/x-javascript": WebInspector.Resource.Type.Script,
737     "application/json": WebInspector.Resource.Type.Script,
738     "application/x-json": WebInspector.Resource.Type.Script,
739     "text/x-javascript": WebInspector.Resource.Type.Script,
740     "text/x-json": WebInspector.Resource.Type.Script,
741     "text/javascript1.1": WebInspector.Resource.Type.Script,
742     "text/javascript1.2": WebInspector.Resource.Type.Script,
743     "text/javascript1.3": WebInspector.Resource.Type.Script,
744     "text/jscript": WebInspector.Resource.Type.Script,
745     "text/livescript": WebInspector.Resource.Type.Script,
746     "text/x-livescript": WebInspector.Resource.Type.Script,
747     "text/typescript": WebInspector.Resource.Type.Script,
748     "text/x-clojure": WebInspector.Resource.Type.Script,
749     "text/x-coffeescript": WebInspector.Resource.Type.Script
750 };