1/*
2 * Copyright (C) 2012 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @constructor
33 * @implements {WebInspector.TargetManager.Observer}
34 * @param {!WebInspector.NetworkWorkspaceBinding} networkWorkspaceBinding
35 * @param {!WebInspector.Workspace} workspace
36 */
37WebInspector.NetworkUISourceCodeProvider = function(networkWorkspaceBinding, workspace)
38{
39    this._networkWorkspaceBinding = networkWorkspaceBinding;
40    this._workspace = workspace;
41    WebInspector.targetManager.observeTargets(this);
42    this._processedURLs = {};
43}
44
45WebInspector.NetworkUISourceCodeProvider.prototype = {
46    /**
47     * @param {!WebInspector.Target} target
48     */
49    targetAdded: function(target)
50    {
51        target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
52        target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
53        target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
54        target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
55    },
56
57    /**
58     * @param {!WebInspector.Target} target
59     */
60    targetRemoved: function(target)
61    {
62        // FIXME: add workspace cleanup here.
63        target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
64        target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
65        target.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
66        target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
67    },
68
69    _populate: function()
70    {
71        /**
72         * @param {!WebInspector.ResourceTreeFrame} frame
73         * @this {WebInspector.NetworkUISourceCodeProvider}
74         */
75        function populateFrame(frame)
76        {
77            for (var i = 0; i < frame.childFrames.length; ++i)
78                populateFrame.call(this, frame.childFrames[i]);
79
80            var resources = frame.resources();
81            for (var i = 0; i < resources.length; ++i)
82                this._resourceAdded({data:resources[i]});
83        }
84
85        populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
86    },
87
88    /**
89     * @param {!WebInspector.Event} event
90     */
91    _parsedScriptSource: function(event)
92    {
93        var script = /** @type {!WebInspector.Script} */ (event.data);
94        if (!script.sourceURL || script.isInlineScript() || script.isSnippet())
95            return;
96        // Filter out embedder injected content scripts.
97        if (script.isContentScript() && !script.hasSourceURL) {
98            var parsedURL = new WebInspector.ParsedURL(script.sourceURL);
99            if (!parsedURL.isValid)
100                return;
101        }
102        this._addFile(script.sourceURL, script, script.isContentScript());
103    },
104
105    /**
106     * @param {!WebInspector.Event} event
107     */
108    _styleSheetAdded: function(event)
109    {
110        var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
111        if ((!header.hasSourceURL || header.isInline) && header.origin !== "inspector")
112            return;
113
114        this._addFile(header.resourceURL(), header, false);
115    },
116
117    /**
118     * @param {!WebInspector.Event|!{data: !WebInspector.Resource}} event
119     */
120    _resourceAdded: function(event)
121    {
122        var resource = /** @type {!WebInspector.Resource} */ (event.data);
123        this._addFile(resource.url, new WebInspector.NetworkUISourceCodeProvider.FallbackResource(resource));
124    },
125
126    /**
127     * @param {!WebInspector.Event} event
128     */
129    _mainFrameNavigated: function(event)
130    {
131        this._reset();
132    },
133
134    /**
135     * @param {string} url
136     * @param {!WebInspector.ContentProvider} contentProvider
137     * @param {boolean=} isContentScript
138     */
139    _addFile: function(url, contentProvider, isContentScript)
140    {
141        if (this._workspace.hasMappingForURL(url))
142            return;
143
144        var type = contentProvider.contentType();
145        if (type !== WebInspector.resourceTypes.Stylesheet && type !== WebInspector.resourceTypes.Document && type !== WebInspector.resourceTypes.Script)
146            return;
147        if (this._processedURLs[url])
148            return;
149        this._processedURLs[url] = true;
150        this._networkWorkspaceBinding.addFileForURL(url, contentProvider, isContentScript);
151    },
152
153    _reset: function()
154    {
155        this._processedURLs = {};
156        this._networkWorkspaceBinding.reset();
157        this._populate();
158    }
159}
160
161/**
162 * @constructor
163 * @implements {WebInspector.ContentProvider}
164 * @param {!WebInspector.Resource} resource
165 */
166WebInspector.NetworkUISourceCodeProvider.FallbackResource = function(resource)
167{
168    this._resource = resource;
169}
170
171WebInspector.NetworkUISourceCodeProvider.FallbackResource.prototype = {
172
173    /**
174     * @return {string}
175     */
176    contentURL: function()
177    {
178        return this._resource.contentURL();
179    },
180
181    /**
182     * @return {!WebInspector.ResourceType}
183     */
184    contentType: function()
185    {
186        return this._resource.contentType();
187    },
188
189    /**
190     * @param {function(?string)} callback
191     */
192    requestContent: function(callback)
193    {
194        /**
195         * @this {WebInspector.NetworkUISourceCodeProvider.FallbackResource}
196         */
197        function loadFallbackContent()
198        {
199            var scripts = WebInspector.debuggerModel.scriptsForSourceURL(this._resource.url);
200            if (!scripts.length) {
201                callback(null);
202                return;
203            }
204
205            var contentProvider;
206            if (this._resource.type === WebInspector.resourceTypes.Document)
207                contentProvider = new WebInspector.ConcatenatedScriptsContentProvider(scripts);
208            else if (this._resource.type === WebInspector.resourceTypes.Script)
209                contentProvider = scripts[0];
210
211            console.assert(contentProvider, "Resource content request failed. " + this._resource.url);
212
213            contentProvider.requestContent(callback);
214        }
215
216        /**
217         * @param {?string} content
218         * @this {WebInspector.NetworkUISourceCodeProvider.FallbackResource}
219         */
220        function requestContentLoaded(content)
221        {
222            if (content)
223                callback(content)
224            else
225                loadFallbackContent.call(this);
226        }
227
228        this._resource.requestContent(requestContentLoaded.bind(this));
229    },
230
231    /**
232     * @param {string} query
233     * @param {boolean} caseSensitive
234     * @param {boolean} isRegex
235     * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
236     */
237    searchInContent: function(query, caseSensitive, isRegex, callback)
238    {
239        /**
240         * @param {?string} content
241         */
242        function documentContentLoaded(content)
243        {
244            if (content === null) {
245                callback([]);
246                return;
247            }
248
249            var result = WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex);
250            callback(result);
251        }
252
253        if (this.contentType() === WebInspector.resourceTypes.Document) {
254            this.requestContent(documentContentLoaded);
255            return;
256        }
257
258        this._resource.searchInContent(query, caseSensitive, isRegex, callback);
259    }
260}
261
262/**
263 * @type {!WebInspector.NetworkWorkspaceBinding}
264 */
265WebInspector.networkWorkspaceBinding;
266