8340c35cae6a45aab06f28efdbc7ea21738beccc
[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     requestContentFromBackend: function(callback)
114     {
115         if (!this._id) {
116             // There is no identifier to request content with. Return false to cause the
117             // pending callbacks to get null content.
118             return Promise.reject({ message: "There is no identifier to request content with." });
119         }
120
121         return DebuggerAgent.getScriptSource.promise(this._id);
122     },
123
124     saveIdentityToCookie: function(cookie)
125     {
126         cookie[WebInspector.Script.URLCookieKey] = this.url;
127         cookie[WebInspector.Script.DisplayNameCookieKey] = this.displayName;
128     },
129
130     requestScriptSyntaxTree: function(callback)
131     {
132         if (this._scriptSyntaxTree) {
133             setTimeout(function() { callback(this._scriptSyntaxTree); }.bind(this), 0);
134             return;
135         }
136
137         var makeSyntaxTreeAndCallCallback = function(content)
138         {
139             this._makeSyntaxTree(content);
140             callback(this._scriptSyntaxTree);
141         }.bind(this);
142
143         var content = this.content;
144         if (!content && this._resource && this._resource.type === WebInspector.Resource.Type.Script && this._resource.finished)
145             content = this._resource.content;
146         if (content) {
147             setTimeout(makeSyntaxTreeAndCallCallback, 0, content);
148             return;
149         }
150
151         this.requestContent().then(function(parameters) {
152             makeSyntaxTreeAndCallCallback(parameters.content);
153         }).catch(function(error) {
154             makeSyntaxTreeAndCallCallback(null);
155         });
156     },
157
158     // Private
159
160     _resolveResource: function()
161     {
162         // FIXME: We should be able to associate a Script with a Resource through identifiers,
163         // we shouldn't need to lookup by URL, which is not safe with frames, where there might
164         // be multiple resources with the same URL.
165         // <rdar://problem/13373951> Scripts should be able to associate directly with a Resource
166
167         // No URL, no resource.
168         if (!this._url)
169             return null;
170
171         try {
172             // Try with the Script's full URL.
173             var resource = WebInspector.frameResourceManager.resourceForURL(this.url);
174             if (resource)
175                 return resource;
176
177             // Try with the Script's full decoded URL.
178             var decodedURL = decodeURI(this._url);
179             if (decodedURL !== this._url) {
180                 resource = WebInspector.frameResourceManager.resourceForURL(decodedURL);
181                 if (resource)
182                     return resource;
183             }
184
185             // Next try removing any fragment in the original URL.
186             var urlWithoutFragment = removeURLFragment(this._url);
187             if (urlWithoutFragment !== this._url) {
188                 resource = WebInspector.frameResourceManager.resourceForURL(urlWithoutFragment);
189                 if (resource)
190                     return resource;
191             }
192
193             // Finally try removing any fragment in the decoded URL.
194             var decodedURLWithoutFragment = removeURLFragment(decodedURL);
195             if (decodedURLWithoutFragment !== decodedURL) {
196                 resource = WebInspector.frameResourceManager.resourceForURL(decodedURLWithoutFragment);
197                 if (resource)
198                     return resource;
199             }
200         } catch (e) {
201             // Ignore possible URIErrors.
202         }
203
204         return null;
205     },
206
207     _makeSyntaxTree: function(sourceText)
208     {
209         if (this._scriptSyntaxTree || !sourceText)
210             return;
211
212         this._scriptSyntaxTree = new WebInspector.ScriptSyntaxTree(sourceText, this);
213     }
214 };
215
216 WebInspector.Script.prototype.__proto__ = WebInspector.SourceCode.prototype;