Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js

Issue 1641893002: DevTools: [SASS] introduce workspace/cssModel adapter for SASS processor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: remove settimeout from test Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
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.SourceMapTracker>} */
25 this._urlToTrackers = 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.SourceMapTracker}
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._sassUISourceCode(sourceURL);
71 if (contentProvider)
72 this._contentProviderAdded(sourceURL);
73 }
74
75 var tracker = new WebInspector.SourceMapTracker(this, sourceMap);
76 for (var sourceURL of tracker.allURLs())
77 this._urlToTrackers.set(sourceURL, tracker);
78 return tracker;
79 },
80
81 /**
82 * @param {!WebInspector.SourceMapTracker} tracker
83 */
84 _stopTrackSources: function(tracker)
85 {
86 for (var sourceURL of tracker.allURLs()) {
87 this._urlToTrackers.remove(sourceURL, tracker);
88 if (!this._urlToTrackers.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 _sassUISourceCode: function(url)
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 trackers = new Set(this._urlToTrackers.get(url));
131 for (var tracker of trackers)
132 tracker.dispose();
133 },
134
135 /**
136 * @param {string} url
137 * @return {boolean}
138 */
139 _isSASSURL: function(url)
140 {
141 return this._versions.has(url) && !this._cssURLs.has(url);
142 },
143
144 /**
145 * @param {!WebInspector.Event} event
146 */
147 _uiSourceCodeAdded: function(event)
148 {
149 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data) ;
150 var url = this._networkMapping.networkURL(uiSourceCode);
151 if (!this._isSASSURL(url))
152 return;
153 this._contentProviderAdded(url);
154 },
155
156 /**
157 * @param {!WebInspector.Event} event
158 */
159 _uiSourceCodeRemoved: function(event)
160 {
161 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data) ;
162 var url = this._networkMapping.networkURL(uiSourceCode);
163 if (!this._isSASSURL(url))
164 return;
165 this._contentProviderRemoved(url);
166 },
167
168 /**
169 * @param {!WebInspector.Event} event
170 */
171 _styleSheetAdded: function(event)
172 {
173 var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */( event.data);
174 var url = styleSheetHeader.sourceURL;
175 if (!this._cssURLs.has(url))
176 return;
177 this._contentProviderAdded(url);
178 },
179
180 /**
181 * @param {!WebInspector.Event} event
182 */
183 _styleSheetRemoved: function(event)
184 {
185 var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */( event.data);
186 var url = styleSheetHeader.sourceURL;
187 if (!this._cssURLs.has(url))
188 return;
189 var headers = this._headersForURL(url);
190 if (headers.length)
191 return;
192 this._contentProviderRemoved(url);
193 },
194
195 /**
196 * @param {!WebInspector.Event} event
197 */
198 _uiSourceCodeChanged: function(event)
199 {
200 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data. uiSourceCode);
201 var url = this._networkMapping.networkURL(uiSourceCode);
202 if (!this._isSASSURL(url))
203 return;
204 this._newContentAvailable(url);
205 },
206
207 /**
208 * @param {!WebInspector.Event} event
209 */
210 _styleSheetChanged: function(event)
211 {
212 var styleSheetId = /** @type {!CSSAgent.StyleSheetId} */(event.data.styl eSheetId);
213 var styleSheetHeader = this._cssModel.styleSheetHeaderForId(styleSheetId );
214 var url = styleSheetHeader.sourceURL;
215 if (!this._cssURLs.has(url))
216 return;
217 this._newContentAvailable(url);
218 },
219
220 /**
221 * @param {string} url
222 */
223 _newContentAvailable: function(url)
224 {
225 console.assert(this._versions.has(url), "The '" + url + "' is not tracke d.")
226 var newVersion = this._versions.get(url) + 1;
227 this._versions.set(url, newVersion);
228 for (var tracker of this._urlToTrackers.get(url))
229 tracker._newContentAvailable(url, newVersion);
230 },
231
232 /**
233 * @param {string} url
234 * @return {number}
235 */
236 _urlVersion: function(url)
237 {
238 var version = this._versions.get(url);
239 console.assert(version, "The '" + url + "' is not tracked.")
240 return version || 0;
241 },
242
243 /**
244 * @param {string} url
245 * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>}
246 */
247 _getContent: function(url)
248 {
249 console.assert(this._awaitingPromises.has(url), "The '" + url + "' is no t tracked.")
250 return this._awaitingPromises.get(url)
251 .then(onContentProviderResolved.bind(this));
252
253 /**
254 * @param {boolean} success
255 * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse> }
256 * @this {WebInspector.SASSWorkspaceAdapter}
257 */
258 function onContentProviderResolved(success)
259 {
260 if (!success)
261 return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAda pter.ContentResponse} */(null));
262 var contentProvider = this._cssURLs.has(url) ? this._headersForURL(u rl).peekLast() : this._sassUISourceCode(url);
263 if (!contentProvider)
264 return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAda pter.ContentResponse} */(null));
265 return contentProvider.requestContent()
266 .then(text => new WebInspector.SASSWorkspaceAdapter.ContentRespo nse(url, /** @type {number} */(this._versions.get(url)), text || ""));
267 }
268 },
269
270 /**
271 * @param {string} url
272 * @param {string} text
273 * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse}
274 */
275 _setSASSText: function(url, text)
276 {
277 console.assert(this._isSASSURL(url), "The url '" + url + "' should be a tracked SASS url");
278 var uiSourceCode = this._sassUISourceCode(url);
279 if (!uiSourceCode)
280 return null;
281 setImmediate(() => uiSourceCode.addRevision(text));
282 var futureVersion = this._versions.get(url) + 1;
283 return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, future Version, text);
284 },
285
286 /**
287 * @param {string} url
288 * @param {string} text
289 * @param {!Array<!WebInspector.SourceEdit>} cssEdits
290 * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse}
291 */
292 _setCSSText: function(url, text, cssEdits)
293 {
294 console.assert(this._cssURLs.has(url), "The url '" + url + "' should be a tracked CSS url");
295 var headers = this._headersForURL(url);
296 if (!headers.length)
297 return null;
298 for (var i = 0; i < headers.length; ++i)
299 this._cssModel.setStyleSheetText(headers[i].id, text, true);
300 for (var i = cssEdits.length - 1; i >= 0; --i) {
301 var edit = cssEdits[i];
302 var oldRange = edit.oldRange;
303 var newRange = edit.newRange();
304 for (var j = 0; j < headers.length; ++j) {
305 this._cssModel.dispatchEventToListeners(WebInspector.CSSStyleMod el.Events.ExternalRangeEdit, {
306 styleSheetId: headers[j].id,
307 oldRange: oldRange,
308 newRange: newRange
309 });
310 }
311 }
312 var futureVersion = this._versions.get(url) + headers.length;
313 return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, future Version, text);
314 }
315 }
316
317 /**
318 * @constructor
319 * @extends {WebInspector.Object}
320 * @param {!WebInspector.SASSWorkspaceAdapter} adapter
321 * @param {!WebInspector.SourceMap} sourceMap
322 */
323 WebInspector.SourceMapTracker = function(adapter, sourceMap)
324 {
325 WebInspector.Object.call(this);
326 this._adapter = adapter;
327 this._sourceMap = sourceMap;
328 this._cssURL = sourceMap.compiledURL();
329 this._sassURLs = sourceMap.sources().slice();
330 this._allURLs = this._sassURLs.concat(this._cssURL);
331 this._terminated = false;
332 this._versions = new Map();
333 for (var url of this._allURLs)
334 this._versions.set(url, adapter._urlVersion(url));
335 }
336
337 /** @enum {string} */
338 WebInspector.SourceMapTracker.Events = {
339 SourceChanged: "SourceChanged",
340 TrackingStopped: "TrackingStopped"
341 }
342
343 WebInspector.SourceMapTracker.prototype = {
344 /**
345 * @return {!WebInspector.SourceMap}
346 */
347 sourceMap: function()
348 {
349 return this._sourceMap;
350 },
351
352 /**
353 * @return {!Array<string>}
354 */
355 allURLs: function()
356 {
357 return this._allURLs;
358 },
359
360 /**
361 * @return {string}
362 */
363 cssURL: function()
364 {
365 return this._cssURL;
366 },
367
368 /**
369 * @return {!Array<string>}
370 */
371 sassURLs: function()
372 {
373 return this._sassURLs;
374 },
375
376 /**
377 * @return {boolean}
378 */
379 isOutdated: function()
380 {
381 if (this._terminated)
382 return true;
383 for (var url of this._allURLs) {
384 if (this._adapter._urlVersion(url) > this._versions.get(url))
385 return true;
386 }
387 return false;
388 },
389
390 /**
391 * @param {string} text
392 * @param {!Array<!WebInspector.SourceEdit>} edits
393 * @return {boolean}
394 */
395 setCSSText: function(text, edits)
396 {
397 if (this._terminated || this.isOutdated())
398 return false;
399 var result = this._adapter._setCSSText(this._cssURL, text, edits);
400 this._handleContentResponse(result);
401 return !!result;
402 },
403
404 /**
405 * @param {string} url
406 * @param {string} text
407 * @return {boolean}
408 */
409 setSASSText: function(url, text)
410 {
411 if (this._terminated || this.isOutdated())
412 return false;
413 var result = this._adapter._setSASSText(url, text);
414 this._handleContentResponse(result);
415 return !!result;
416 },
417
418 /**
419 * @param {?WebInspector.SASSWorkspaceAdapter.ContentResponse} contentRespon se
420 * @return {?string}
421 */
422 _handleContentResponse: function(contentResponse)
423 {
424 if (!contentResponse)
425 return null;
426 this._versions.set(contentResponse.url, contentResponse.version);
427 return contentResponse.text;
428 },
429
430 /**
431 * @param {string} url
432 * @return {!Promise<string>}
433 */
434 content: function(url)
435 {
436 return this._adapter._getContent(url)
437 .then(this._handleContentResponse.bind(this))
438 .then(text => text || "");
439 },
440
441 dispose: function()
442 {
443 if (this._terminated)
444 return;
445 this._terminated = true;
446 this._adapter._stopTrackSources(this);
447 this.dispatchEventToListeners(WebInspector.SourceMapTracker.Events.Track ingStopped);
448 },
449
450 /**
451 * @param {string} url
452 * @param {number} newVersion
453 */
454 _newContentAvailable: function(url, newVersion)
455 {
456 if (this._versions.get(url) < newVersion)
457 this.dispatchEventToListeners(WebInspector.SourceMapTracker.Events.S ourceChanged, url);
458 },
459
460 __proto__: WebInspector.Object.prototype
461 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698