OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
dgozman
2016/01/27 23:14:04
2016
lushnikov
2016/01/28 00:04:29
Done.
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @constructor | |
7 * @param {!WebInspector.CSSStyleModel} cssModel | |
8 * @param {!WebInspector.Workspace} workspace | |
9 * @param {!WebInspector.NetworkMapping} networkMapping | |
10 */ | |
11 WebInspector.SASSWorkspaceAdapter = function(cssModel, workspace, networkMapping ) | |
12 { | |
13 this._workspace = workspace; | |
14 this._networkMapping = networkMapping; | |
15 this._cssModel = cssModel; | |
16 | |
17 /** @type {!Map<string, number>} */ | |
18 this._versions = new Map(); | |
19 /** @type {!Map<string, !Promise<boolean>>} */ | |
20 this._awaitingPromises = new Map(); | |
21 /** @type {!Map<string, function(boolean)>} */ | |
22 this._awaitingFulfills = new Map(); | |
23 | |
24 /** @type {!Multimap<string, !WebInspector.SASSWorkspaceAdapter.Client>} */ | |
25 this._urlToClients = new Multimap(); | |
26 /** @type {!Set<string>} */ | |
27 this._cssURLs = new Set(); | |
28 | |
29 this._eventListeners = [ | |
30 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceC odeAdded, this._uiSourceCodeAdded, this), | |
31 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceC odeRemoved, this._uiSourceCodeRemoved, this), | |
32 this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCo pyChanged, this._uiSourceCodeChanged, this), | |
33 this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCo pyCommitted, this._uiSourceCodeChanged, this), | |
34 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleS heetAdded, this._styleSheetAdded, this), | |
35 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleS heetRemoved, this._styleSheetRemoved, this), | |
36 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleS heetChanged, this._styleSheetChanged, this) | |
37 ]; | |
38 } | |
39 | |
40 /** | |
41 * @constructor | |
42 * @param {string} url | |
43 * @param {number} version | |
44 * @param {string} text | |
45 */ | |
46 WebInspector.SASSWorkspaceAdapter.ContentResponse = function(url, version, text) | |
47 { | |
48 this.url = url; | |
49 this.version = version; | |
50 this.text = text; | |
51 } | |
52 | |
53 WebInspector.SASSWorkspaceAdapter.prototype = { | |
54 /** | |
55 * @param {!WebInspector.SourceMap} sourceMap | |
56 * @return {!WebInspector.SASSWorkspaceAdapter.Client} | |
dgozman
2016/01/27 23:14:04
WebInspector.SourceMapTracker
lushnikov
2016/01/28 00:04:29
Done.
| |
57 */ | |
58 trackSources: function(sourceMap) | |
59 { | |
60 var cssURL = sourceMap.compiledURL(); | |
61 this._cssURLs.add(cssURL); | |
62 | |
63 var allSources = new Set(sourceMap.sources().concat(cssURL)); | |
64 for (var sourceURL of allSources) { | |
65 if (this._versions.has(sourceURL)) | |
66 continue; | |
67 this._versions.set(sourceURL, 1); | |
68 var promise = new Promise(fulfill => this._awaitingFulfills.set(sour ceURL, fulfill)); | |
69 this._awaitingPromises.set(sourceURL, promise); | |
70 var contentProvider = sourceURL === cssURL ? this._headersForURL(sou rceURL).peekLast() : this._sourceCodeForURL(sourceURL); | |
71 if (contentProvider) | |
72 this._contentProviderAdded(sourceURL); | |
73 } | |
74 | |
75 var client = new WebInspector.SASSWorkspaceAdapter.Client(this, sourceMa p); | |
76 for (var sourceURL of client.allURLs()) | |
77 this._urlToClients.set(sourceURL, client); | |
78 return client; | |
79 }, | |
80 | |
81 /** | |
82 * @param {!WebInspector.SASSWorkspaceAdapter.Client} client | |
83 */ | |
84 _stopTrackSources: function(client) | |
85 { | |
86 for (var sourceURL of client.allURLs()) { | |
87 this._urlToClients.remove(sourceURL, client); | |
88 if (!this._urlToClients.has(sourceURL)) { | |
89 this._awaitingFulfills.get(sourceURL).call(null, false); | |
90 this._awaitingFulfills.delete(sourceURL); | |
91 this._awaitingPromises.delete(sourceURL); | |
92 this._versions.delete(sourceURL); | |
93 this._cssURLs.delete(sourceURL); | |
94 } | |
95 } | |
96 }, | |
97 | |
98 /** | |
99 * @param {string} url | |
100 * @return {?WebInspector.UISourceCode} | |
101 */ | |
102 _sourceCodeForURL: function(url) | |
dgozman
2016/01/27 23:14:04
sassUISourceCode(url)
lushnikov
2016/01/28 00:04:29
Done.
| |
103 { | |
104 return this._networkMapping.uiSourceCodeForURLForAnyTarget(url); | |
105 }, | |
106 | |
107 /** | |
108 * @param {string} url | |
109 * @return {!Array<!WebInspector.CSSStyleSheetHeader>} | |
110 */ | |
111 _headersForURL: function(url) | |
112 { | |
113 return this._cssModel.styleSheetIdsForURL(url) | |
114 .map(styleSheetId => this._cssModel.styleSheetHeaderForId(styleSheet Id)); | |
115 }, | |
116 | |
117 /** | |
118 * @param {string} url | |
119 */ | |
120 _contentProviderAdded: function(url) | |
121 { | |
122 this._awaitingFulfills.get(url).call(null, true); | |
123 }, | |
124 | |
125 /** | |
126 * @param {string} url | |
127 */ | |
128 _contentProviderRemoved: function(url) | |
129 { | |
130 var clients = new Set(this._urlToClients.get(url)); | |
131 for (var client of clients) | |
132 client.dispose(client); | |
dgozman
2016/01/27 23:14:04
client.dispose()
lushnikov
2016/01/28 00:04:30
Done.
| |
133 }, | |
134 | |
135 /** | |
136 * @param {!WebInspector.Event} event | |
137 */ | |
138 _uiSourceCodeAdded: function(event) | |
139 { | |
140 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data) ; | |
141 var url = this._networkMapping.networkURL(uiSourceCode); | |
142 if (this._cssURLs.has(url) || !this._versions.has(url)) | |
dgozman
2016/01/27 23:14:04
this._isSassURL(url) or comment about it.
lushnikov
2016/01/28 00:04:29
Done.
| |
143 return; | |
144 this._contentProviderAdded(url); | |
145 }, | |
146 | |
147 /** | |
148 * @param {!WebInspector.Event} event | |
149 */ | |
150 _uiSourceCodeRemoved: function(event) | |
151 { | |
152 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data) ; | |
153 var url = this._networkMapping.networkURL(uiSourceCode); | |
154 if (this._cssURLs.has(url) || !this._versions.has(url)) | |
155 return; | |
156 this._contentProviderRemoved(url); | |
157 }, | |
158 | |
159 /** | |
160 * @param {!WebInspector.Event} event | |
161 */ | |
162 _styleSheetAdded: function(event) | |
163 { | |
164 var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */( event.data); | |
165 var url = styleSheetHeader.sourceURL; | |
166 if (!this._cssURLs.has(url)) | |
167 return; | |
168 this._contentProviderAdded(url); | |
169 }, | |
170 | |
171 /** | |
172 * @param {!WebInspector.Event} event | |
173 */ | |
174 _styleSheetRemoved: function(event) | |
175 { | |
176 var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */( event.data); | |
177 var url = styleSheetHeader.sourceURL; | |
178 if (!this._cssURLs.has(url)) | |
179 return; | |
180 var headers = this._headersForURL(url); | |
181 if (headers.length) | |
182 return; | |
183 this._contentProviderRemoved(url); | |
184 }, | |
185 | |
186 /** | |
187 * @param {!WebInspector.Event} event | |
188 */ | |
189 _uiSourceCodeChanged: function(event) | |
190 { | |
191 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data. uiSourceCode); | |
192 var url = this._networkMapping.networkURL(uiSourceCode); | |
193 if (this._cssURLs.has(url) || !this._versions.has(url)) | |
dgozman
2016/01/27 23:14:04
isSassURL
lushnikov
2016/01/28 00:04:29
Done.
| |
194 return; | |
195 this._newContentAvailable(url); | |
196 }, | |
197 | |
198 /** | |
199 * @param {!WebInspector.Event} event | |
200 */ | |
201 _styleSheetChanged: function(event) | |
202 { | |
203 var styleSheetId = /** @type {!CSSAgent.StyleSheetId} */(event.data.styl eSheetId); | |
204 var styleSheetHeader = this._cssModel.styleSheetHeaderForId(styleSheetId ); | |
205 var url = styleSheetHeader.sourceURL; | |
206 if (!this._cssURLs.has(url)) | |
207 return; | |
208 this._newContentAvailable(url); | |
209 }, | |
210 | |
211 /** | |
212 * @param {string} url | |
213 */ | |
214 _newContentAvailable: function(url) | |
215 { | |
216 var newVersion = this._versions.get(url) + 1; | |
dgozman
2016/01/27 23:14:04
console.assert(this._versions.has(url))
lushnikov
2016/01/28 00:04:30
Done.
| |
217 this._versions.set(url, newVersion); | |
218 for (var client of this._urlToClients.get(url)) | |
219 client._newContentAvailable(url, newVersion); | |
220 }, | |
221 | |
222 /** | |
223 * @param {string} url | |
224 * @return {number} | |
225 */ | |
226 _urlVersion: function(url) | |
227 { | |
228 var version = this._versions.get(url); | |
229 console.assert(version, "The '" + url + "' is not tracked.") | |
230 return version || 0; | |
231 }, | |
232 | |
233 /** | |
234 * @param {string} url | |
235 * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>} | |
236 */ | |
237 _getContent: function(url) | |
238 { | |
239 console.assert(this._awaitingPromises.has(url), "The '" + url + "' is no t tracked.") | |
240 return this._awaitingPromises.get(url) | |
241 .then(onContentProviderResolved.bind(this)); | |
242 | |
243 /** | |
244 * @param {boolean} success | |
245 * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse> } | |
246 * @this {WebInspector.SASSWorkspaceAdapter} | |
247 */ | |
248 function onContentProviderResolved(success) | |
249 { | |
250 if (!success) | |
251 return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAda pter.ContentResponse} */(null)); | |
252 var contentProvider = this._cssURLs.has(url) ? this._headersForURL(u rl).peekLast() : this._sourceCodeForURL(url); | |
253 if (!contentProvider) | |
254 return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAda pter.ContentResponse} */(null)); | |
255 return contentProvider.requestContent() | |
256 .then(text => new WebInspector.SASSWorkspaceAdapter.ContentRespo nse(url, /** @type {number} */(this._versions.get(url)), text || "")); | |
257 } | |
258 }, | |
259 | |
260 /** | |
261 * @param {string} url | |
262 * @param {string} text | |
263 * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} | |
264 */ | |
265 _setSASSText: function(url, text) | |
266 { | |
267 console.assert(this._versions.has(url) && !this._cssURLs.has(url), "The url '" + url + "' should be a tracked SASS url"); | |
dgozman
2016/01/27 23:14:04
isSassURL
lushnikov
2016/01/28 00:04:29
Done.
| |
268 var uiSourceCode = this._sourceCodeForURL(url); | |
269 if (!uiSourceCode) | |
270 return null; | |
271 setImmediate(() => uiSourceCode.addRevision(text)); | |
272 var futureVersion = this._versions.get(url) + 1; | |
273 return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, future Version, text); | |
274 }, | |
275 | |
276 /** | |
277 * @param {string} url | |
278 * @param {string} text | |
279 * @param {!Array<!WebInspector.SourceEdit>} cssEdits | |
280 * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} | |
281 */ | |
282 _setCSSText: function(url, text, cssEdits) | |
283 { | |
284 console.assert(this._cssURLs.has(url), "The url '" + url + "' should be a tracked CSS url"); | |
285 var headers = this._headersForURL(url); | |
286 if (!headers.length) | |
287 return null; | |
288 for (var i = 0; i < headers.length; ++i) | |
289 headers[i].setContent(text, function() { }); | |
290 for (var i = cssEdits.length - 1; i >= 0; --i) { | |
291 var edit = cssEdits[i]; | |
292 var oldRange = edit.oldRange; | |
293 var newRange = edit.newRange(); | |
294 for (var j = 0; j < headers.length; ++j) { | |
295 this._cssModel.dispatchEventToListeners(WebInspector.CSSStyleMod el.Events.ExternalRangeEdit, { | |
296 styleSheetId: headers[j].id, | |
297 oldRange: oldRange, | |
298 newRange: newRange | |
299 }); | |
300 } | |
301 } | |
302 var futureVersion = this._versions.get(url) + headers.length; | |
303 return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, future Version, text); | |
304 } | |
305 } | |
306 | |
307 /** @enum {string} */ | |
308 WebInspector.SASSWorkspaceAdapter.ClientEvents = { | |
309 SourceChanged: "SourceChanged", | |
310 TrackingStopped: "TrackingStopped" | |
311 } | |
312 | |
313 /** | |
314 * @constructor | |
315 * @extends {WebInspector.Object} | |
316 * @param {!WebInspector.SASSWorkspaceAdapter} adapter | |
317 * @param {!WebInspector.SourceMap} sourceMap | |
318 */ | |
319 WebInspector.SASSWorkspaceAdapter.Client = function(adapter, sourceMap) | |
320 { | |
321 WebInspector.Object.call(this); | |
322 this._adapter = adapter; | |
323 this._cssURL = sourceMap.compiledURL(); | |
324 this._sassURLs = sourceMap.sources().slice(); | |
325 this._allURLs = this._sassURLs.concat(this._cssURL); | |
326 this._terminated = false; | |
327 this._versions = new Map(); | |
328 for (var url of this._allURLs) | |
329 this._versions.set(url, adapter._urlVersion(url)); | |
330 } | |
331 | |
332 WebInspector.SASSWorkspaceAdapter.Client.prototype = { | |
333 /** | |
334 * @return {!Array<string>} | |
335 */ | |
336 allURLs: function() | |
337 { | |
338 return this._allURLs; | |
339 }, | |
340 | |
341 /** | |
342 * @return {string} | |
343 */ | |
344 cssURL: function() | |
345 { | |
346 return this._cssURL; | |
347 }, | |
348 | |
349 /** | |
350 * @return {!Array<string>} | |
351 */ | |
352 sassURLs: function() | |
353 { | |
354 return this._sassURLs; | |
355 }, | |
356 | |
357 /** | |
358 * @return {boolean} | |
359 */ | |
360 isOutdated: function() | |
361 { | |
362 if (this._terminated) | |
363 return true; | |
364 for (var url of this._allURLs) { | |
365 if (this._adapter._urlVersion(url) > this._versions.get(url)) | |
366 return true; | |
367 } | |
368 return false; | |
369 }, | |
370 | |
371 /** | |
372 * @param {string} text | |
373 * @param {!Array<!WebInspector.SourceEdit>} edits | |
374 * @return {boolean} | |
375 */ | |
376 setCSSText: function(text, edits) | |
377 { | |
378 if (this._terminated || this.isOutdated()) | |
379 return false; | |
380 var result = this._adapter._setCSSText(this._cssURL, text, edits); | |
381 return !!this._handleContentResponse(result); | |
dgozman
2016/01/27 23:14:04
this._handleContentResponse(result);
return !!resu
lushnikov
2016/01/28 00:04:29
Done.
| |
382 }, | |
383 | |
384 /** | |
385 * @param {string} url | |
386 * @param {string} text | |
387 * @return {boolean} | |
388 */ | |
389 setSASSText: function(url, text) | |
390 { | |
391 if (this._terminated || this.isOutdated()) | |
392 return false; | |
393 var result = this._adapter._setSASSText(url, text); | |
394 return !!this._handleContentResponse(result); | |
395 }, | |
396 | |
397 /** | |
398 * @param {?WebInspector.SASSWorkspaceAdapter.ContentResponse} contentRespon se | |
399 * @return {?string} | |
400 */ | |
401 _handleContentResponse: function(contentResponse) | |
402 { | |
403 if (!contentResponse) | |
404 return null; | |
405 this._versions.set(contentResponse.url, contentResponse.version); | |
406 return contentResponse.text; | |
407 }, | |
408 | |
409 /** | |
410 * @param {string} url | |
411 * @return {!Promise<string>} | |
412 */ | |
413 content: function(url) | |
414 { | |
415 return this._adapter._getContent(url) | |
416 .then(this._handleContentResponse.bind(this)) | |
417 .then(text => text || ""); | |
418 }, | |
419 | |
420 dispose: function() | |
421 { | |
422 if (this._terminated) | |
423 return; | |
424 this._terminated = true; | |
425 this._adapter._stopTrackSources(this); | |
426 this.dispatchEventToListeners(WebInspector.SASSWorkspaceAdapter.ClientEv ents.TrackingStopped); | |
427 }, | |
428 | |
429 /** | |
430 * @param {string} url | |
431 * @param {number} newVersion | |
432 */ | |
433 _newContentAvailable: function(url, newVersion) | |
434 { | |
435 if (this._versions.get(url) < newVersion) | |
436 this.dispatchEventToListeners(WebInspector.SASSWorkspaceAdapter.Clie ntEvents.SourceChanged, url); | |
437 }, | |
438 | |
439 __proto__: WebInspector.Object.prototype | |
440 } | |
OLD | NEW |