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
29/**
30 * @constructor
31 * @extends {WebInspector.Object}
32 * @implements {WebInspector.ContentProvider}
33 * @param {?WebInspector.NetworkRequest} request
34 * @param {string} url
35 * @param {string} documentURL
36 * @param {!PageAgent.FrameId} frameId
37 * @param {!NetworkAgent.LoaderId} loaderId
38 * @param {!WebInspector.ResourceType} type
39 * @param {string} mimeType
40 * @param {boolean=} isHidden
41 */
42WebInspector.Resource = function(request, url, documentURL, frameId, loaderId, type, mimeType, isHidden)
43{
44    this._request = request;
45    this.url = url;
46    this._documentURL = documentURL;
47    this._frameId = frameId;
48    this._loaderId = loaderId;
49    this._type = type || WebInspector.resourceTypes.Other;
50    this._mimeType = mimeType;
51    this._isHidden = isHidden;
52
53    /** @type {?string} */ this._content;
54    /** @type {boolean} */ this._contentEncoded;
55    this._pendingContentCallbacks = [];
56    if (this._request && !this._request.finished)
57        this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._requestFinished, this);
58}
59
60WebInspector.Resource.Events = {
61    MessageAdded: "message-added",
62    MessagesCleared: "messages-cleared",
63}
64
65WebInspector.Resource.prototype = {
66    /**
67     * @return {?WebInspector.NetworkRequest}
68     */
69    get request()
70    {
71        return this._request;
72    },
73
74    /**
75     * @return {string}
76     */
77    get url()
78    {
79        return this._url;
80    },
81
82    set url(x)
83    {
84        this._url = x;
85        this._parsedURL = new WebInspector.ParsedURL(x);
86    },
87
88    get parsedURL()
89    {
90        return this._parsedURL;
91    },
92
93    /**
94     * @return {string}
95     */
96    get documentURL()
97    {
98        return this._documentURL;
99    },
100
101    /**
102     * @return {!PageAgent.FrameId}
103     */
104    get frameId()
105    {
106        return this._frameId;
107    },
108
109    /**
110     * @return {!NetworkAgent.LoaderId}
111     */
112    get loaderId()
113    {
114        return this._loaderId;
115    },
116
117    /**
118     * @return {string}
119     */
120    get displayName()
121    {
122        return this._parsedURL.displayName;
123    },
124
125    /**
126     * @return {!WebInspector.ResourceType}
127     */
128    get type()
129    {
130        return this._request ? this._request.type : this._type;
131    },
132
133    /**
134     * @return {string}
135     */
136    get mimeType()
137    {
138        return this._request ? this._request.mimeType : this._mimeType;
139    },
140
141    /**
142     * @return {!Array.<!WebInspector.ConsoleMessage>}
143     */
144    get messages()
145    {
146        return this._messages || [];
147    },
148
149    /**
150     * @param {!WebInspector.ConsoleMessage} msg
151     */
152    addMessage: function(msg)
153    {
154        if (!msg.isErrorOrWarning() || !msg.message)
155            return;
156
157        if (!this._messages)
158            this._messages = [];
159        this._messages.push(msg);
160        this.dispatchEventToListeners(WebInspector.Resource.Events.MessageAdded, msg);
161    },
162
163    /**
164     * @return {number}
165     */
166    get errors()
167    {
168        return this._errors || 0;
169    },
170
171    set errors(x)
172    {
173        this._errors = x;
174    },
175
176    /**
177     * @return {number}
178     */
179    get warnings()
180    {
181        return this._warnings || 0;
182    },
183
184    set warnings(x)
185    {
186        this._warnings = x;
187    },
188
189    clearErrorsAndWarnings: function()
190    {
191        this._messages = [];
192        this._warnings = 0;
193        this._errors = 0;
194        this.dispatchEventToListeners(WebInspector.Resource.Events.MessagesCleared);
195    },
196
197    /**
198     * @return {?string}
199     */
200    get content()
201    {
202        return this._content;
203    },
204
205    /**
206     * @return {boolean}
207     */
208    get contentEncoded()
209    {
210        return this._contentEncoded;
211    },
212
213    /**
214     * @return {string}
215     */
216    contentURL: function()
217    {
218        return this._url;
219    },
220
221    /**
222     * @return {!WebInspector.ResourceType}
223     */
224    contentType: function()
225    {
226        return this.type;
227    },
228
229    /**
230     * @param {function(?string)} callback
231     */
232    requestContent: function(callback)
233    {
234        if (typeof this._content !== "undefined") {
235            callback(this._content);
236            return;
237        }
238
239        this._pendingContentCallbacks.push(callback);
240        if (!this._request || this._request.finished)
241            this._innerRequestContent();
242    },
243
244    canonicalMimeType: function()
245    {
246        return this.type.canonicalMimeType() || this.mimeType;
247    },
248
249    /**
250     * @param {string} query
251     * @param {boolean} caseSensitive
252     * @param {boolean} isRegex
253     * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
254     */
255    searchInContent: function(query, caseSensitive, isRegex, callback)
256    {
257        /**
258         * @param {?Protocol.Error} error
259         * @param {!Array.<!PageAgent.SearchMatch>} searchMatches
260         */
261        function callbackWrapper(error, searchMatches)
262        {
263            callback(searchMatches || []);
264        }
265
266        if (this.type === WebInspector.resourceTypes.Document) {
267            this.requestContent(documentContentLoaded);
268            return;
269        }
270
271        /**
272         * @param {?string} content
273         */
274        function documentContentLoaded(content)
275        {
276            if (content === null) {
277                callback([]);
278                return;
279            }
280
281            var result = WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex);
282            callback(result);
283        }
284
285        if (this.frameId)
286            PageAgent.searchInResource(this.frameId, this.url, query, caseSensitive, isRegex, callbackWrapper);
287        else
288            callback([]);
289    },
290
291    /**
292     * @param {!Element} image
293     */
294    populateImageSource: function(image)
295    {
296        /**
297         * @param {?string} content
298         * @this {WebInspector.Resource}
299         */
300        function onResourceContent(content)
301        {
302            var imageSrc = WebInspector.contentAsDataURL(this._content, this.mimeType, this._contentEncoded);
303            if (imageSrc === null)
304                imageSrc = this.url;
305            image.src = imageSrc;
306        }
307
308        this.requestContent(onResourceContent.bind(this));
309    },
310
311    _requestFinished: function()
312    {
313        this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._requestFinished, this);
314        if (this._pendingContentCallbacks.length)
315            this._innerRequestContent();
316    },
317
318
319    _innerRequestContent: function()
320    {
321        if (this._contentRequested)
322            return;
323        this._contentRequested = true;
324
325        /**
326         * @param {?Protocol.Error} error
327         * @param {?string} content
328         * @param {boolean} contentEncoded
329         * @this {WebInspector.Resource}
330         */
331        function contentLoaded(error, content, contentEncoded)
332        {
333            if (error || content === null) {
334                loadFallbackContent.call(this, error);
335                return;
336            }
337            replyWithContent.call(this, content, contentEncoded);
338        }
339
340        /**
341         * @param {?string} content
342         * @param {boolean} contentEncoded
343         * @this {WebInspector.Resource}
344         */
345        function replyWithContent(content, contentEncoded)
346        {
347            this._content = content;
348            this._contentEncoded = contentEncoded;
349            var callbacks = this._pendingContentCallbacks.slice();
350            for (var i = 0; i < callbacks.length; ++i)
351                callbacks[i](this._content);
352            this._pendingContentCallbacks.length = 0;
353            delete this._contentRequested;
354        }
355
356        /**
357         * @param {?Protocol.Error} error
358         * @param {string} content
359         * @param {boolean} contentEncoded
360         * @this {WebInspector.Resource}
361         */
362        function resourceContentLoaded(error, content, contentEncoded)
363        {
364            contentLoaded.call(this, error, content, contentEncoded);
365        }
366
367        /**
368         * @param {?Protocol.Error} error
369         * @this {WebInspector.Resource}
370         */
371        function loadFallbackContent(error)
372        {
373            var scripts = WebInspector.debuggerModel.scriptsForSourceURL(this.url);
374            if (!scripts.length) {
375                console.error("Resource content request failed: " + error);
376                replyWithContent.call(this, null, false);
377                return;
378            }
379
380            var contentProvider;
381            if (this.type === WebInspector.resourceTypes.Document)
382                contentProvider = new WebInspector.ConcatenatedScriptsContentProvider(scripts);
383            else if (this.type === WebInspector.resourceTypes.Script)
384                contentProvider = scripts[0];
385
386            if (!contentProvider) {
387                console.error("Resource content request failed: " + error);
388                replyWithContent.call(this, null, false);
389                return;
390            }
391
392            contentProvider.requestContent(fallbackContentLoaded.bind(this));
393        }
394
395        /**
396         * @param {?string} content
397         * @this {WebInspector.Resource}
398         */
399        function fallbackContentLoaded(content)
400        {
401            replyWithContent.call(this, content, false);
402        }
403
404        if (this.request) {
405            this.request.requestContent(requestContentLoaded.bind(this));
406            return;
407        }
408
409        /**
410         * @param {?string} content
411         * @this {WebInspector.Resource}
412         */
413        function requestContentLoaded(content)
414        {
415            contentLoaded.call(this, null, content, this.request.contentEncoded);
416        }
417
418        PageAgent.getResourceContent(this.frameId, this.url, resourceContentLoaded.bind(this));
419    },
420
421    /**
422     * @return {boolean}
423     */
424    isHidden: function()
425    {
426        return !!this._isHidden;
427    },
428
429    __proto__: WebInspector.Object.prototype
430}
431
432