b4c9e29fece029da99699006ff827c04882ed0de
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Models / Script.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.Script = function(id, range, url, injected, sourceMapURL)
27 {
28     WebInspector.SourceCode.call(this);
29
30     console.assert(id);
31     console.assert(range instanceof WebInspector.TextRange);
32
33     this._id = id || null;
34     this._range = range || null;
35     this._url = url || null;
36     this._injected = injected || false;
37
38     this._resource = this._resolveResource();
39     if (this._resource)
40         this._resource.associateWithScript(this);
41
42     if (sourceMapURL)
43         WebInspector.sourceMapManager.downloadSourceMap(sourceMapURL, this._url, this);
44
45     this._scriptSyntaxTree = null;
46 };
47
48 WebInspector.Script.TypeIdentifier = "script";
49 WebInspector.Script.URLCookieKey = "script-url";
50 WebInspector.Script.DisplayNameCookieKey = "script-display-name";
51
52 WebInspector.Script.resetUniqueDisplayNameNumbers = function()
53 {
54     WebInspector.Script._nextUniqueDisplayNameNumber = 1;
55 };
56
57 WebInspector.Script._nextUniqueDisplayNameNumber = 1;
58
59 WebInspector.Script.prototype = {
60     constructor: WebInspector.Script,
61
62     // Public
63
64     get id()
65     {
66         return this._id;
67     },
68
69     get range()
70     {
71         return this._range;
72     },
73
74     get url()
75     {
76         return this._url;
77     },
78
79     get urlComponents()
80     {
81         if (!this._urlComponents)
82             this._urlComponents = parseURL(this._url);
83         return this._urlComponents;
84     },
85
86     get displayName()
87     {
88         if (this._url)
89             return WebInspector.displayNameForURL(this._url, this.urlComponents);
90
91         // Assign a unique number to the script object so it will stay the same.
92         if (!this._uniqueDisplayNameNumber)
93             this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
94
95         return WebInspector.UIString("Anonymous Script %d").format(this._uniqueDisplayNameNumber);
96     },
97
98     get injected()
99     {
100         return this._injected;
101     },
102
103     get resource()
104     {
105         return this._resource;
106     },
107
108     get scriptSyntaxTree()
109     {
110         return this._scriptSyntaxTree;
111     },
112
113     canRequestContentFromBackend: function()
114     {
115         // We can request content if we have an id.
116         return !!this._id;
117     },
118
119     requestContentFromBackend: function(callback)
120     {
121         if (!this._id) {
122             // There is no identifier to request content with. Return false to cause the
123             // pending callbacks to get null content.
124             return false;
125         }
126
127         DebuggerAgent.getScriptSource(this._id, callback);
128         return true;
129     },
130
131     saveIdentityToCookie: function(cookie)
132     {
133         cookie[WebInspector.Script.URLCookieKey] = this.url;
134         cookie[WebInspector.Script.DisplayNameCookieKey] = this.displayName;
135     },
136
137     requestScriptSyntaxTree: function(callback)
138     {
139         if (this._scriptSyntaxTree) {
140             setTimeout(function() { callback(this._scriptSyntaxTree); }.bind(this), 0);
141             return;
142         }
143
144         var makeSyntaxTreeAndCallCallback = function(content)
145         {
146             this._makeSyntaxTree(content);
147             callback(this._scriptSyntaxTree);
148         }.bind(this);
149
150         var content = this.content;
151         if (!content && this._resource && this._resource.type === WebInspector.Resource.Type.Script && this._resource.finished)
152             content = this._resource.content;
153         if (content) {
154             setTimeout(makeSyntaxTreeAndCallCallback, 0, content);
155             return;
156         }
157
158         this.requestContent(function(error, sourceText) {
159             makeSyntaxTreeAndCallCallback(error ? null : sourceText);
160         });
161     },
162
163     // Private
164
165     _resolveResource: function()
166     {
167         // FIXME: We should be able to associate a Script with a Resource through identifiers,
168         // we shouldn't need to lookup by URL, which is not safe with frames, where there might
169         // be multiple resources with the same URL.
170         // <rdar://problem/13373951> Scripts should be able to associate directly with a Resource
171
172         // No URL, no resource.
173         if (!this._url)
174             return null;
175
176         try {
177             // Try with the Script's full URL.
178             var resource = WebInspector.frameResourceManager.resourceForURL(this.url);
179             if (resource)
180                 return resource;
181
182             // Try with the Script's full decoded URL.
183             var decodedURL = decodeURI(this._url);
184             if (decodedURL !== this._url) {
185                 resource = WebInspector.frameResourceManager.resourceForURL(decodedURL);
186                 if (resource)
187                     return resource;
188             }
189
190             // Next try removing any fragment in the original URL.
191             var urlWithoutFragment = removeURLFragment(this._url);
192             if (urlWithoutFragment !== this._url) {
193                 resource = WebInspector.frameResourceManager.resourceForURL(urlWithoutFragment);
194                 if (resource)
195                     return resource;
196             }
197
198             // Finally try removing any fragment in the decoded URL.
199             var decodedURLWithoutFragment = removeURLFragment(decodedURL);
200             if (decodedURLWithoutFragment !== decodedURL) {
201                 resource = WebInspector.frameResourceManager.resourceForURL(decodedURLWithoutFragment);
202                 if (resource)
203                     return resource;
204             }
205         } catch (e) {
206             // Ignore possible URIErrors.
207         }
208
209         return null;
210     },
211
212     _makeSyntaxTree: function(sourceText)
213     {
214         if (this._scriptSyntaxTree || !sourceText)
215             return;
216
217         this._scriptSyntaxTree = new WebInspector.ScriptSyntaxTree(sourceText, this);
218     }
219 };
220
221 WebInspector.Script.prototype.__proto__ = WebInspector.SourceCode.prototype;