Web Inspector: SourceCode.requestContent should return a promise
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Models / SourceCode.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.SourceCode = function()
27 {
28     WebInspector.Object.call(this);
29
30     this._originalRevision = new WebInspector.SourceCodeRevision(this, null, false);
31     this._currentRevision = this._originalRevision;
32
33     this._sourceMaps = null;
34     this._formatterSourceMap = null;
35     this._requestContentPromise = null;
36 };
37
38 WebInspector.Object.addConstructorFunctions(WebInspector.SourceCode);
39
40 WebInspector.SourceCode.Event = {
41     ContentDidChange: "source-code-content-did-change",
42     SourceMapAdded: "source-code-source-map-added",
43     FormatterDidChange: "source-code-formatter-did-change",
44     LoadingDidFinish: "source-code-loading-did-finish",
45     LoadingDidFail: "source-code-loading-did-fail"
46 };
47
48 WebInspector.SourceCode.prototype = {
49     constructor: WebInspector.SourceCode,
50
51     // Public
52
53     get displayName()
54     {
55         // Implemented by subclasses.
56         console.error("Needs to be implemented by a subclass.");
57         return "";
58     },
59
60     get originalRevision()
61     {
62         return this._originalRevision;
63     },
64
65     get currentRevision()
66     {
67         return this._currentRevision;
68     },
69
70     set currentRevision(revision)
71     {
72         console.assert(revision instanceof WebInspector.SourceCodeRevision);
73         if (!(revision instanceof WebInspector.SourceCodeRevision))
74             return;
75
76         console.assert(revision.sourceCode === this);
77         if (revision.sourceCode !== this)
78             return;
79
80         this._currentRevision = revision;
81
82         this.dispatchEventToListeners(WebInspector.SourceCode.Event.ContentDidChange);
83     },
84
85     get content()
86     {
87         return this._currentRevision.content;
88     },
89
90     get contentIsBase64Encoded()
91     {
92         return this._currentRevision.contentIsBase64Encoded;
93     },
94
95     get sourceMaps()
96     {
97         return this._sourceMaps || [];
98     },
99
100     addSourceMap: function(sourceMap)
101     {
102         console.assert(sourceMap instanceof WebInspector.SourceMap);
103
104         if (!this._sourceMaps)
105             this._sourceMaps = [];
106
107         this._sourceMaps.push(sourceMap);
108
109         this.dispatchEventToListeners(WebInspector.SourceCode.Event.SourceMapAdded);
110     },
111
112     get formatterSourceMap()
113     {
114         return this._formatterSourceMap;
115     },
116
117     set formatterSourceMap(formatterSourceMap)
118     {
119         console.assert(this._formatterSourceMap === null || formatterSourceMap === null);
120         console.assert(formatterSourceMap === null || formatterSourceMap instanceof WebInspector.FormatterSourceMap);
121
122         this._formatterSourceMap = formatterSourceMap;
123
124         this.dispatchEventToListeners(WebInspector.SourceCode.Event.FormatterDidChange);
125     },
126
127     requestContent: function()
128     {
129         this._requestContentPromise = this._requestContentPromise || this.requestContentFromBackend().then(this._processContent.bind(this));
130
131         return this._requestContentPromise;
132     },
133
134     createSourceCodeLocation: function(lineNumber, columnNumber)
135     {
136         return new WebInspector.SourceCodeLocation(this, lineNumber, columnNumber);
137     },
138
139     createLazySourceCodeLocation: function(lineNumber, columnNumber)
140     {
141         return new WebInspector.LazySourceCodeLocation(this, lineNumber, columnNumber);
142     },
143
144     createSourceCodeTextRange: function(textRange)
145     {
146         return new WebInspector.SourceCodeTextRange(this, textRange);
147     },
148
149     // Protected
150
151     revisionContentDidChange: function(revision)
152     {
153         if (this._ignoreRevisionContentDidChangeEvent)
154             return;
155
156         if (revision !== this._currentRevision)
157             return;
158
159         this.handleCurrentRevisionContentChange();
160
161         this.dispatchEventToListeners(WebInspector.SourceCode.Event.ContentDidChange);
162     },
163
164     handleCurrentRevisionContentChange: function()
165     {
166         // Implemented by subclasses if needed.
167     },
168
169     get revisionForRequestedContent()
170     {
171         // Implemented by subclasses if needed.
172         return this._originalRevision;
173     },
174
175     markContentAsStale: function()
176     {
177         this._contentReceived = false;
178     },
179
180     requestContentFromBackend: function()
181     {
182         // Implemented by subclasses.
183         console.error("Needs to be implemented by a subclass.");
184         return Promise.reject(new Error("Needs to be implemented by a subclass."));
185     },
186
187     // Private
188
189     _processContent: function(parameters)
190     {
191         // Different backend APIs return one of `content, `body`, or `scriptSource`.
192         var content = parameters.content || parameters.body || parameters.scriptSource;
193         var base64Encoded = parameters.base64Encoded;
194
195         var revision = this.revisionForRequestedContent;
196
197         this._ignoreRevisionContentDidChangeEvent = true;
198         revision.content = content || null;
199         revision.contentIsBase64Encoded = base64Encoded || false;
200         delete this._ignoreRevisionContentDidChangeEvent;
201
202         return Promise.resolve({
203             sourceCode: this,
204             content: content,
205             base64Encoded: base64Encoded
206         });
207     }
208 };
209
210 WebInspector.SourceCode.prototype.__proto__ = WebInspector.Object.prototype;