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