Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..67c789b5b010f122b8623e40735ea204ab8b43bf |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js |
| @@ -0,0 +1,440 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
|
dgozman
2016/01/27 23:14:04
2016
lushnikov
2016/01/28 00:04:29
Done.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| + * @constructor |
| + * @param {!WebInspector.CSSStyleModel} cssModel |
| + * @param {!WebInspector.Workspace} workspace |
| + * @param {!WebInspector.NetworkMapping} networkMapping |
| + */ |
| +WebInspector.SASSWorkspaceAdapter = function(cssModel, workspace, networkMapping) |
| +{ |
| + this._workspace = workspace; |
| + this._networkMapping = networkMapping; |
| + this._cssModel = cssModel; |
| + |
| + /** @type {!Map<string, number>} */ |
| + this._versions = new Map(); |
| + /** @type {!Map<string, !Promise<boolean>>} */ |
| + this._awaitingPromises = new Map(); |
| + /** @type {!Map<string, function(boolean)>} */ |
| + this._awaitingFulfills = new Map(); |
| + |
| + /** @type {!Multimap<string, !WebInspector.SASSWorkspaceAdapter.Client>} */ |
| + this._urlToClients = new Multimap(); |
| + /** @type {!Set<string>} */ |
| + this._cssURLs = new Set(); |
| + |
| + this._eventListeners = [ |
| + this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this), |
| + this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this), |
| + this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyChanged, this._uiSourceCodeChanged, this), |
| + this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyCommitted, this._uiSourceCodeChanged, this), |
| + this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this), |
| + this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this), |
| + this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this) |
| + ]; |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @param {string} url |
| + * @param {number} version |
| + * @param {string} text |
| + */ |
| +WebInspector.SASSWorkspaceAdapter.ContentResponse = function(url, version, text) |
| +{ |
| + this.url = url; |
| + this.version = version; |
| + this.text = text; |
| +} |
| + |
| +WebInspector.SASSWorkspaceAdapter.prototype = { |
| + /** |
| + * @param {!WebInspector.SourceMap} sourceMap |
| + * @return {!WebInspector.SASSWorkspaceAdapter.Client} |
|
dgozman
2016/01/27 23:14:04
WebInspector.SourceMapTracker
lushnikov
2016/01/28 00:04:29
Done.
|
| + */ |
| + trackSources: function(sourceMap) |
| + { |
| + var cssURL = sourceMap.compiledURL(); |
| + this._cssURLs.add(cssURL); |
| + |
| + var allSources = new Set(sourceMap.sources().concat(cssURL)); |
| + for (var sourceURL of allSources) { |
| + if (this._versions.has(sourceURL)) |
| + continue; |
| + this._versions.set(sourceURL, 1); |
| + var promise = new Promise(fulfill => this._awaitingFulfills.set(sourceURL, fulfill)); |
| + this._awaitingPromises.set(sourceURL, promise); |
| + var contentProvider = sourceURL === cssURL ? this._headersForURL(sourceURL).peekLast() : this._sourceCodeForURL(sourceURL); |
| + if (contentProvider) |
| + this._contentProviderAdded(sourceURL); |
| + } |
| + |
| + var client = new WebInspector.SASSWorkspaceAdapter.Client(this, sourceMap); |
| + for (var sourceURL of client.allURLs()) |
| + this._urlToClients.set(sourceURL, client); |
| + return client; |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.SASSWorkspaceAdapter.Client} client |
| + */ |
| + _stopTrackSources: function(client) |
| + { |
| + for (var sourceURL of client.allURLs()) { |
| + this._urlToClients.remove(sourceURL, client); |
| + if (!this._urlToClients.has(sourceURL)) { |
| + this._awaitingFulfills.get(sourceURL).call(null, false); |
| + this._awaitingFulfills.delete(sourceURL); |
| + this._awaitingPromises.delete(sourceURL); |
| + this._versions.delete(sourceURL); |
| + this._cssURLs.delete(sourceURL); |
| + } |
| + } |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @return {?WebInspector.UISourceCode} |
| + */ |
| + _sourceCodeForURL: function(url) |
|
dgozman
2016/01/27 23:14:04
sassUISourceCode(url)
lushnikov
2016/01/28 00:04:29
Done.
|
| + { |
| + return this._networkMapping.uiSourceCodeForURLForAnyTarget(url); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @return {!Array<!WebInspector.CSSStyleSheetHeader>} |
| + */ |
| + _headersForURL: function(url) |
| + { |
| + return this._cssModel.styleSheetIdsForURL(url) |
| + .map(styleSheetId => this._cssModel.styleSheetHeaderForId(styleSheetId)); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + */ |
| + _contentProviderAdded: function(url) |
| + { |
| + this._awaitingFulfills.get(url).call(null, true); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + */ |
| + _contentProviderRemoved: function(url) |
| + { |
| + var clients = new Set(this._urlToClients.get(url)); |
| + for (var client of clients) |
| + client.dispose(client); |
|
dgozman
2016/01/27 23:14:04
client.dispose()
lushnikov
2016/01/28 00:04:30
Done.
|
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _uiSourceCodeAdded: function(event) |
| + { |
| + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data); |
| + var url = this._networkMapping.networkURL(uiSourceCode); |
| + 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.
|
| + return; |
| + this._contentProviderAdded(url); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _uiSourceCodeRemoved: function(event) |
| + { |
| + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data); |
| + var url = this._networkMapping.networkURL(uiSourceCode); |
| + if (this._cssURLs.has(url) || !this._versions.has(url)) |
| + return; |
| + this._contentProviderRemoved(url); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _styleSheetAdded: function(event) |
| + { |
| + var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */(event.data); |
| + var url = styleSheetHeader.sourceURL; |
| + if (!this._cssURLs.has(url)) |
| + return; |
| + this._contentProviderAdded(url); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _styleSheetRemoved: function(event) |
| + { |
| + var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */(event.data); |
| + var url = styleSheetHeader.sourceURL; |
| + if (!this._cssURLs.has(url)) |
| + return; |
| + var headers = this._headersForURL(url); |
| + if (headers.length) |
| + return; |
| + this._contentProviderRemoved(url); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _uiSourceCodeChanged: function(event) |
| + { |
| + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data.uiSourceCode); |
| + var url = this._networkMapping.networkURL(uiSourceCode); |
| + 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.
|
| + return; |
| + this._newContentAvailable(url); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.Event} event |
| + */ |
| + _styleSheetChanged: function(event) |
| + { |
| + var styleSheetId = /** @type {!CSSAgent.StyleSheetId} */(event.data.styleSheetId); |
| + var styleSheetHeader = this._cssModel.styleSheetHeaderForId(styleSheetId); |
| + var url = styleSheetHeader.sourceURL; |
| + if (!this._cssURLs.has(url)) |
| + return; |
| + this._newContentAvailable(url); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + */ |
| + _newContentAvailable: function(url) |
| + { |
| + 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.
|
| + this._versions.set(url, newVersion); |
| + for (var client of this._urlToClients.get(url)) |
| + client._newContentAvailable(url, newVersion); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @return {number} |
| + */ |
| + _urlVersion: function(url) |
| + { |
| + var version = this._versions.get(url); |
| + console.assert(version, "The '" + url + "' is not tracked.") |
| + return version || 0; |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>} |
| + */ |
| + _getContent: function(url) |
| + { |
| + console.assert(this._awaitingPromises.has(url), "The '" + url + "' is not tracked.") |
| + return this._awaitingPromises.get(url) |
| + .then(onContentProviderResolved.bind(this)); |
| + |
| + /** |
| + * @param {boolean} success |
| + * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>} |
| + * @this {WebInspector.SASSWorkspaceAdapter} |
| + */ |
| + function onContentProviderResolved(success) |
| + { |
| + if (!success) |
| + return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAdapter.ContentResponse} */(null)); |
| + var contentProvider = this._cssURLs.has(url) ? this._headersForURL(url).peekLast() : this._sourceCodeForURL(url); |
| + if (!contentProvider) |
| + return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAdapter.ContentResponse} */(null)); |
| + return contentProvider.requestContent() |
| + .then(text => new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, /** @type {number} */(this._versions.get(url)), text || "")); |
| + } |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @param {string} text |
| + * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} |
| + */ |
| + _setSASSText: function(url, text) |
| + { |
| + 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.
|
| + var uiSourceCode = this._sourceCodeForURL(url); |
| + if (!uiSourceCode) |
| + return null; |
| + setImmediate(() => uiSourceCode.addRevision(text)); |
| + var futureVersion = this._versions.get(url) + 1; |
| + return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, futureVersion, text); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @param {string} text |
| + * @param {!Array<!WebInspector.SourceEdit>} cssEdits |
| + * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} |
| + */ |
| + _setCSSText: function(url, text, cssEdits) |
| + { |
| + console.assert(this._cssURLs.has(url), "The url '" + url + "' should be a tracked CSS url"); |
| + var headers = this._headersForURL(url); |
| + if (!headers.length) |
| + return null; |
| + for (var i = 0; i < headers.length; ++i) |
| + headers[i].setContent(text, function() { }); |
| + for (var i = cssEdits.length - 1; i >= 0; --i) { |
| + var edit = cssEdits[i]; |
| + var oldRange = edit.oldRange; |
| + var newRange = edit.newRange(); |
| + for (var j = 0; j < headers.length; ++j) { |
| + this._cssModel.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.ExternalRangeEdit, { |
| + styleSheetId: headers[j].id, |
| + oldRange: oldRange, |
| + newRange: newRange |
| + }); |
| + } |
| + } |
| + var futureVersion = this._versions.get(url) + headers.length; |
| + return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, futureVersion, text); |
| + } |
| +} |
| + |
| +/** @enum {string} */ |
| +WebInspector.SASSWorkspaceAdapter.ClientEvents = { |
| + SourceChanged: "SourceChanged", |
| + TrackingStopped: "TrackingStopped" |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @extends {WebInspector.Object} |
| + * @param {!WebInspector.SASSWorkspaceAdapter} adapter |
| + * @param {!WebInspector.SourceMap} sourceMap |
| + */ |
| +WebInspector.SASSWorkspaceAdapter.Client = function(adapter, sourceMap) |
| +{ |
| + WebInspector.Object.call(this); |
| + this._adapter = adapter; |
| + this._cssURL = sourceMap.compiledURL(); |
| + this._sassURLs = sourceMap.sources().slice(); |
| + this._allURLs = this._sassURLs.concat(this._cssURL); |
| + this._terminated = false; |
| + this._versions = new Map(); |
| + for (var url of this._allURLs) |
| + this._versions.set(url, adapter._urlVersion(url)); |
| +} |
| + |
| +WebInspector.SASSWorkspaceAdapter.Client.prototype = { |
| + /** |
| + * @return {!Array<string>} |
| + */ |
| + allURLs: function() |
| + { |
| + return this._allURLs; |
| + }, |
| + |
| + /** |
| + * @return {string} |
| + */ |
| + cssURL: function() |
| + { |
| + return this._cssURL; |
| + }, |
| + |
| + /** |
| + * @return {!Array<string>} |
| + */ |
| + sassURLs: function() |
| + { |
| + return this._sassURLs; |
| + }, |
| + |
| + /** |
| + * @return {boolean} |
| + */ |
| + isOutdated: function() |
| + { |
| + if (this._terminated) |
| + return true; |
| + for (var url of this._allURLs) { |
| + if (this._adapter._urlVersion(url) > this._versions.get(url)) |
| + return true; |
| + } |
| + return false; |
| + }, |
| + |
| + /** |
| + * @param {string} text |
| + * @param {!Array<!WebInspector.SourceEdit>} edits |
| + * @return {boolean} |
| + */ |
| + setCSSText: function(text, edits) |
| + { |
| + if (this._terminated || this.isOutdated()) |
| + return false; |
| + var result = this._adapter._setCSSText(this._cssURL, text, edits); |
| + return !!this._handleContentResponse(result); |
|
dgozman
2016/01/27 23:14:04
this._handleContentResponse(result);
return !!resu
lushnikov
2016/01/28 00:04:29
Done.
|
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @param {string} text |
| + * @return {boolean} |
| + */ |
| + setSASSText: function(url, text) |
| + { |
| + if (this._terminated || this.isOutdated()) |
| + return false; |
| + var result = this._adapter._setSASSText(url, text); |
| + return !!this._handleContentResponse(result); |
| + }, |
| + |
| + /** |
| + * @param {?WebInspector.SASSWorkspaceAdapter.ContentResponse} contentResponse |
| + * @return {?string} |
| + */ |
| + _handleContentResponse: function(contentResponse) |
| + { |
| + if (!contentResponse) |
| + return null; |
| + this._versions.set(contentResponse.url, contentResponse.version); |
| + return contentResponse.text; |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @return {!Promise<string>} |
| + */ |
| + content: function(url) |
| + { |
| + return this._adapter._getContent(url) |
| + .then(this._handleContentResponse.bind(this)) |
| + .then(text => text || ""); |
| + }, |
| + |
| + dispose: function() |
| + { |
| + if (this._terminated) |
| + return; |
| + this._terminated = true; |
| + this._adapter._stopTrackSources(this); |
| + this.dispatchEventToListeners(WebInspector.SASSWorkspaceAdapter.ClientEvents.TrackingStopped); |
| + }, |
| + |
| + /** |
| + * @param {string} url |
| + * @param {number} newVersion |
| + */ |
| + _newContentAvailable: function(url, newVersion) |
| + { |
| + if (this._versions.get(url) < newVersion) |
| + this.dispatchEventToListeners(WebInspector.SASSWorkspaceAdapter.ClientEvents.SourceChanged, url); |
| + }, |
| + |
| + __proto__: WebInspector.Object.prototype |
| +} |