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

Unified Diff: Source/devtools/front_end/bindings/SASSSourceMapping.js

Issue 1344833004: DevTools: extract PollManager from SASSSourceMapping (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 5 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/devtools/front_end/bindings/SASSSourceMapping.js
diff --git a/Source/devtools/front_end/bindings/SASSSourceMapping.js b/Source/devtools/front_end/bindings/SASSSourceMapping.js
index e7d8fb597f8ba5971861c077d83d60838d464c3f..370fe3bdc50d1608ef166eed7b763a8964d963a0 100644
--- a/Source/devtools/front_end/bindings/SASSSourceMapping.js
+++ b/Source/devtools/front_end/bindings/SASSSourceMapping.js
@@ -38,12 +38,11 @@
*/
WebInspector.SASSSourceMapping = function(cssModel, workspace, networkMapping, networkProject)
{
- this.pollPeriodMs = 30 * 1000;
- this.pollIntervalMs = 200;
this._cssModel = cssModel;
this._workspace = workspace;
this._networkProject = networkProject;
this._addingRevisionCounter = 0;
+ this._pollManager = new WebInspector.SASSSourceMapping.PollManager(this._cssModel, networkMapping, this._updateCSSRevision.bind(this));
this._reset();
WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._fileSaveFinished, this);
WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._toggleSourceMapSupport, this);
@@ -94,531 +93,553 @@ WebInspector.SASSSourceMapping.prototype = {
{
var sassURL = /** @type {string} */ (event.data);
var cssURLs = this._sassURLToCSSURLs.get(sassURL).valuesArray();
- this._sassFileSaved(sassURL, cssURLs, false);
+ this._pollManager.sassFileChanged(sassURL, cssURLs, false);
},
/**
- * @param {string} headerName
- * @param {!Object.<string, string>} headers
- * @return {?string}
+ * @param {!WebInspector.UISourceCode} cssUISourceCode
+ * @param {string} content
+ * @return {boolean}
*/
- _headerValue: function(headerName, headers)
+ _updateCSSRevision: function(cssUISourceCode, content)
{
- headerName = headerName.toLowerCase();
- var value = null;
- for (var name in headers) {
- if (name.toLowerCase() === headerName) {
- value = headers[name];
- break;
- }
- }
- return value;
+ ++this._addingRevisionCounter;
+ cssUISourceCode.addRevision(content);
+ var cssURL = this._networkMapping.networkURL(cssUISourceCode);
+ var completeSourceMapURL = this._completeSourceMapURLForCSSURL[cssURL];
+ if (!completeSourceMapURL)
+ return false;
+ var ids = this._cssModel.styleSheetIdsForURL(cssURL);
+ if (!ids)
+ return false;
+ var headers = [];
+ for (var i = 0; i < ids.length; ++i)
+ headers.push(this._cssModel.styleSheetHeaderForId(ids[i]));
+ this._loadSourceMapAndBindUISourceCode(headers, true, completeSourceMapURL);
+ return true;
},
/**
- * @param {!Object.<string, string>} headers
- * @return {?Date}
+ * @param {!WebInspector.CSSStyleSheetHeader} header
*/
- _lastModified: function(headers)
+ addHeader: function(header)
{
- var lastModifiedHeader = this._headerValue("last-modified", headers);
- if (!lastModifiedHeader)
- return null;
- var lastModified = new Date(lastModifiedHeader);
- if (isNaN(lastModified.getTime()))
- return null;
- return lastModified;
+ if (!header.sourceMapURL || !header.sourceURL || !WebInspector.moduleSetting("cssSourceMapsEnabled").get())
+ return;
+ var completeSourceMapURL = WebInspector.ParsedURL.completeURL(header.sourceURL, header.sourceMapURL);
+ if (!completeSourceMapURL)
+ return;
+ this._completeSourceMapURLForCSSURL[header.sourceURL] = completeSourceMapURL;
+ this._loadSourceMapAndBindUISourceCode([header], false, completeSourceMapURL);
},
/**
- * @param {!Object.<string, string>} headers
- * @param {string} url
- * @return {?Date}
+ * @param {!WebInspector.CSSStyleSheetHeader} header
*/
- _checkLastModified: function(headers, url)
+ removeHeader: function(header)
{
- var lastModified = this._lastModified(headers);
- if (lastModified)
- return lastModified;
+ var sourceURL = header.sourceURL;
+ if (!sourceURL || !header.sourceMapURL || !this._completeSourceMapURLForCSSURL[sourceURL])
+ return;
+ var sourceMap = this._sourceMapByStyleSheetURL[sourceURL];
+ var sources = sourceMap.sources();
+ for (var i = 0; i < sources.length; ++i)
+ this._sassURLToCSSURLs.remove(sources[i], sourceURL);
+ delete this._sourceMapByStyleSheetURL[sourceURL];
+ delete this._completeSourceMapURLForCSSURL[sourceURL];
- var etagMessage = this._headerValue("etag", headers) ? ", \"ETag\" response header found instead" : "";
- var message = String.sprintf("The \"Last-Modified\" response header is missing or invalid for %s%s. The CSS auto-reload functionality will not work correctly.", url, etagMessage);
- WebInspector.console.log(message);
- return null;
+ var completeSourceMapURL = WebInspector.ParsedURL.completeURL(sourceURL, header.sourceMapURL);
+ if (completeSourceMapURL)
+ delete this._sourceMapByURL[completeSourceMapURL];
+ WebInspector.cssWorkspaceBinding.updateLocations(header);
},
/**
- * @param {string} sassURL
- * @param {!Array.<string>} cssURLs
- * @param {boolean} wasLoadedFromFileSystem
+ * @param {!Array.<!WebInspector.CSSStyleSheetHeader>} headersWithSameSourceURL
+ * @param {boolean} forceRebind
+ * @param {string} completeSourceMapURL
*/
- _sassFileSaved: function(sassURL, cssURLs, wasLoadedFromFileSystem)
+ _loadSourceMapAndBindUISourceCode: function(headersWithSameSourceURL, forceRebind, completeSourceMapURL)
{
- if (!cssURLs)
- return;
- if (!WebInspector.moduleSetting("cssReloadEnabled").get())
- return;
-
- var sassFile = this._networkMapping.uiSourceCodeForURL(sassURL, this._cssModel.target());
- console.assert(sassFile);
- if (wasLoadedFromFileSystem)
- sassFile.requestMetadata(metadataReceived.bind(this));
- else
- WebInspector.ResourceLoader.loadUsingTargetUA(sassURL, null, sassLoadedViaNetwork.bind(this));
+ console.assert(headersWithSameSourceURL.length);
+ var sourceURL = headersWithSameSourceURL[0].sourceURL;
+ this._loadSourceMapForStyleSheet(completeSourceMapURL, sourceURL, forceRebind, sourceMapLoaded.bind(this));
/**
- * @param {number} statusCode
- * @param {!Object.<string, string>} headers
- * @param {string} content
+ * @param {?WebInspector.SourceMap} sourceMap
* @this {WebInspector.SASSSourceMapping}
*/
- function sassLoadedViaNetwork(statusCode, headers, content)
+ function sourceMapLoaded(sourceMap)
{
- if (statusCode >= 400) {
- console.error("Could not load content for " + sassURL + " : " + "HTTP status code: " + statusCode);
+ if (!sourceMap)
return;
+
+ this._sourceMapByStyleSheetURL[sourceURL] = sourceMap;
+ for (var i = 0; i < headersWithSameSourceURL.length; ++i) {
+ if (forceRebind)
+ WebInspector.cssWorkspaceBinding.updateLocations(headersWithSameSourceURL[i]);
+ else
+ this._bindUISourceCode(headersWithSameSourceURL[i], sourceMap);
}
- var lastModified = this._checkLastModified(headers, sassURL);
- if (!lastModified)
- return;
- metadataReceived.call(this, lastModified);
}
+ },
+
+ /**
+ * @param {string} completeSourceMapURL
+ * @param {string} completeStyleSheetURL
+ * @param {boolean} forceReload
+ * @param {function(?WebInspector.SourceMap)} callback
+ */
+ _loadSourceMapForStyleSheet: function(completeSourceMapURL, completeStyleSheetURL, forceReload, callback)
+ {
+ var sourceMap = this._sourceMapByURL[completeSourceMapURL];
+ if (sourceMap && !forceReload) {
+ callback(sourceMap);
+ return;
+ }
+
+ var pendingCallbacks = this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
+ if (pendingCallbacks) {
+ pendingCallbacks.push(callback);
+ return;
+ }
+
+ pendingCallbacks = [callback];
+ this._pendingSourceMapLoadingCallbacks[completeSourceMapURL] = pendingCallbacks;
+
+ WebInspector.SourceMap.load(completeSourceMapURL, completeStyleSheetURL, sourceMapLoaded.bind(this));
/**
- * @param {?Date} timestamp
+ * @param {?WebInspector.SourceMap} sourceMap
* @this {WebInspector.SASSSourceMapping}
*/
- function metadataReceived(timestamp)
+ function sourceMapLoaded(sourceMap)
{
- if (!timestamp)
+ var callbacks = this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
+ delete this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
+ if (!callbacks)
return;
-
- var now = Date.now();
- var deadlineMs = now + this.pollPeriodMs;
- var pollData = this._pollDataForSASSURL[sassURL];
- if (pollData) {
- var dataByURL = pollData.dataByURL;
- for (var url in dataByURL)
- clearTimeout(dataByURL[url].timer);
- }
- pollData = { dataByURL: {}, deadlineMs: deadlineMs, sassTimestamp: timestamp };
- this._pollDataForSASSURL[sassURL] = pollData;
- for (var i = 0; i < cssURLs.length; ++i) {
- pollData.dataByURL[cssURLs[i]] = { previousPoll: now };
- this._pollCallback(cssURLs[i], sassURL);
- }
+ if (sourceMap)
+ this._sourceMapByURL[completeSourceMapURL] = sourceMap;
+ else
+ delete this._sourceMapByURL[completeSourceMapURL];
+ for (var i = 0; i < callbacks.length; ++i)
+ callbacks[i](sourceMap);
}
},
/**
- * @param {string} cssURL
- * @param {string} sassURL
+ * @param {!WebInspector.CSSStyleSheetHeader} header
+ * @param {!WebInspector.SourceMap} sourceMap
*/
- _pollCallback: function(cssURL, sassURL)
+ _bindUISourceCode: function(header, sourceMap)
{
- var now;
- var pollData = this._pollDataForSASSURL[sassURL];
- if (!pollData)
- return;
-
- if ((now = new Date().getTime()) > pollData.deadlineMs) {
- WebInspector.console.warn(WebInspector.UIString("%s hasn't been updated in %d seconds.", cssURL, this.pollPeriodMs / 1000));
- this._stopPolling(cssURL, sassURL);
- return;
+ WebInspector.cssWorkspaceBinding.pushSourceMapping(header, this);
+ var cssURL = header.sourceURL;
+ var sources = sourceMap.sources();
+ for (var i = 0; i < sources.length; ++i) {
+ var sassURL = sources[i];
+ this._sassURLToCSSURLs.set(sassURL, cssURL);
+ if (!this._networkMapping.hasMappingForURL(sassURL) && !this._networkMapping.uiSourceCodeForURL(sassURL, header.target())) {
+ var contentProvider = sourceMap.sourceContentProvider(sassURL, WebInspector.resourceTypes.Stylesheet);
+ this._networkProject.addFileForURL(sassURL, contentProvider);
+ }
}
- var nextPoll = this.pollIntervalMs + pollData.dataByURL[cssURL].previousPoll;
- var remainingTimeoutMs = Math.max(0, nextPoll - now);
- pollData.dataByURL[cssURL].previousPoll = now + remainingTimeoutMs;
- pollData.dataByURL[cssURL].timer = setTimeout(this._reloadCSS.bind(this, cssURL, sassURL), remainingTimeoutMs);
},
/**
- * @param {string} cssURL
- * @param {string} sassURL
+ * @override
+ * @param {!WebInspector.CSSLocation} rawLocation
+ * @return {?WebInspector.UILocation}
*/
- _stopPolling: function(cssURL, sassURL)
+ rawLocationToUILocation: function(rawLocation)
{
- var pollData = this._pollDataForSASSURL[sassURL];
- if (!pollData)
- return;
- delete pollData.dataByURL[cssURL];
- if (!Object.keys(pollData.dataByURL).length)
- delete this._pollDataForSASSURL[sassURL];
+ var sourceMap = this._sourceMapByStyleSheetURL[rawLocation.url];
+ if (!sourceMap)
+ return null;
+ var entry = sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber);
+ if (!entry || !entry.sourceURL)
+ return null;
+ var uiSourceCode = this._networkMapping.uiSourceCodeForURL(entry.sourceURL, rawLocation.target());
+ if (!uiSourceCode)
+ return null;
+ return uiSourceCode.uiLocation(entry.sourceLineNumber, entry.sourceColumnNumber);
},
/**
- * @param {string} cssURL
- * @param {string} sassURL
+ * @override
+ * @param {!WebInspector.UISourceCode} uiSourceCode
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ * @return {?WebInspector.CSSLocation}
*/
- _reloadCSS: function(cssURL, sassURL)
+ uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
{
- var cssUISourceCode = this._networkMapping.uiSourceCodeForURL(cssURL, this._cssModel.target());
- if (!cssUISourceCode) {
- WebInspector.console.warn(WebInspector.UIString("%s resource missing. Please reload the page.", cssURL));
- this._stopPolling(cssURL, sassURL)
- return;
- }
-
- if (this._networkMapping.hasMappingForURL(sassURL))
- this._reloadCSSFromFileSystem(cssUISourceCode, sassURL);
- else
- this._reloadCSSFromNetwork(cssUISourceCode, sassURL);
+ return null;
},
/**
- * @param {!WebInspector.UISourceCode} cssUISourceCode
- * @param {string} sassURL
+ * @override
+ * @return {boolean}
*/
- _reloadCSSFromNetwork: function(cssUISourceCode, sassURL)
+ isIdentity: function()
{
- var cssURL = this._networkMapping.networkURL(cssUISourceCode);
- var data = this._pollDataForSASSURL[sassURL];
- if (!data) {
- this._stopPolling(cssURL, sassURL);
- return;
- }
- var headers = { "if-modified-since": new Date(data.sassTimestamp.getTime() - 1000).toUTCString() };
- WebInspector.ResourceLoader.loadUsingTargetUA(cssURL, headers, contentLoaded.bind(this));
-
- /**
- * @param {number} statusCode
- * @param {!Object.<string, string>} headers
- * @param {string} content
- * @this {WebInspector.SASSSourceMapping}
- */
- function contentLoaded(statusCode, headers, content)
- {
- if (statusCode >= 400) {
- console.error("Could not load content for " + cssURL + " : " + "HTTP status code: " + statusCode);
- this._stopPolling(cssURL, sassURL);
- return;
- }
- if (!this._pollDataForSASSURL[sassURL]) {
- this._stopPolling(cssURL, sassURL);
- return;
- }
- if (statusCode === 304) {
- this._pollCallback(cssURL, sassURL);
- return;
- }
- var lastModified = this._checkLastModified(headers, cssURL);
- if (!lastModified) {
- this._stopPolling(cssURL, sassURL);
- return;
- }
- if (lastModified.getTime() < data.sassTimestamp.getTime()) {
- this._pollCallback(cssURL, sassURL);
- return;
- }
- if (this._updateCSSRevision(cssUISourceCode, content, sassURL))
- this._stopPolling(cssURL, sassURL);
- }
- },
-
- /**
- * @param {!WebInspector.UISourceCode} cssUISourceCode
- * @param {string} sassURL
- */
- _reloadCSSFromFileSystem: function(cssUISourceCode, sassURL)
- {
- cssUISourceCode.requestMetadata(metadataCallback.bind(this));
-
- /**
- * @param {?Date} timestamp
- * @this {WebInspector.SASSSourceMapping}
- */
- function metadataCallback(timestamp)
- {
- var cssURL = this._networkMapping.networkURL(cssUISourceCode);
- if (!timestamp) {
- this._pollCallback(cssURL, sassURL);
- return;
- }
- var cssTimestamp = timestamp.getTime();
- var pollData = this._pollDataForSASSURL[sassURL];
- if (!pollData) {
- this._stopPolling(cssURL, sassURL);
- return;
- }
-
- if (cssTimestamp < pollData.sassTimestamp.getTime()) {
- this._pollCallback(cssURL, sassURL);
- return;
- }
-
- cssUISourceCode.requestOriginalContent(contentCallback.bind(this));
-
- /**
- * @param {?string} content
- * @this {WebInspector.SASSSourceMapping}
- */
- function contentCallback(content)
- {
- // Empty string is a valid value, null means error.
- if (content === null)
- return;
- if (this._updateCSSRevision(cssUISourceCode, content, sassURL))
- this._stopPolling(cssURL, sassURL);
- }
- }
+ return false;
},
/**
- * @param {!WebInspector.UISourceCode} cssUISourceCode
- * @param {string} content
- * @param {string} sassURL
+ * @override
+ * @param {!WebInspector.UISourceCode} uiSourceCode
+ * @param {number} lineNumber
* @return {boolean}
*/
- _updateCSSRevision: function(cssUISourceCode, content, sassURL)
+ uiLineHasMapping: function(uiSourceCode, lineNumber)
{
- ++this._addingRevisionCounter;
- cssUISourceCode.addRevision(content);
- var cssURL = this._networkMapping.networkURL(cssUISourceCode);
- var completeSourceMapURL = this._completeSourceMapURLForCSSURL[cssURL];
- if (!completeSourceMapURL)
- return false;
- var ids = this._cssModel.styleSheetIdsForURL(cssURL);
- if (!ids)
- return false;
- var headers = [];
- for (var i = 0; i < ids.length; ++i)
- headers.push(this._cssModel.styleSheetHeaderForId(ids[i]));
- this._loadSourceMapAndBindUISourceCode(headers, true, completeSourceMapURL);
return true;
},
/**
- * @param {!WebInspector.CSSStyleSheetHeader} header
+ * @return {!WebInspector.Target}
*/
- addHeader: function(header)
+ target: function()
{
- if (!header.sourceMapURL || !header.sourceURL || !WebInspector.moduleSetting("cssSourceMapsEnabled").get())
- return;
- var completeSourceMapURL = WebInspector.ParsedURL.completeURL(header.sourceURL, header.sourceMapURL);
- if (!completeSourceMapURL)
- return;
- this._completeSourceMapURLForCSSURL[header.sourceURL] = completeSourceMapURL;
- this._loadSourceMapAndBindUISourceCode([header], false, completeSourceMapURL);
+ return this._cssModel.target();
},
/**
- * @param {!WebInspector.CSSStyleSheetHeader} header
+ * @param {!WebInspector.Event} event
*/
- removeHeader: function(header)
+ _uiSourceCodeAdded: function(event)
{
- var sourceURL = header.sourceURL;
- if (!sourceURL || !header.sourceMapURL || !this._completeSourceMapURLForCSSURL[sourceURL])
+ var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
+ var networkURL = this._networkMapping.networkURL(uiSourceCode);
+ var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray();
+ if (!cssURLs)
return;
- var sourceMap = this._sourceMapByStyleSheetURL[sourceURL];
- var sources = sourceMap.sources();
- for (var i = 0; i < sources.length; ++i)
- this._sassURLToCSSURLs.remove(sources[i], sourceURL);
- delete this._sourceMapByStyleSheetURL[sourceURL];
- delete this._completeSourceMapURLForCSSURL[sourceURL];
-
- var completeSourceMapURL = WebInspector.ParsedURL.completeURL(sourceURL, header.sourceMapURL);
- if (completeSourceMapURL)
- delete this._sourceMapByURL[completeSourceMapURL];
- WebInspector.cssWorkspaceBinding.updateLocations(header);
- },
-
- /**
- * @param {!Array.<!WebInspector.CSSStyleSheetHeader>} headersWithSameSourceURL
- * @param {boolean} forceRebind
- * @param {string} completeSourceMapURL
- */
- _loadSourceMapAndBindUISourceCode: function(headersWithSameSourceURL, forceRebind, completeSourceMapURL)
- {
- console.assert(headersWithSameSourceURL.length);
- var sourceURL = headersWithSameSourceURL[0].sourceURL;
- this._loadSourceMapForStyleSheet(completeSourceMapURL, sourceURL, forceRebind, sourceMapLoaded.bind(this));
-
- /**
- * @param {?WebInspector.SourceMap} sourceMap
- * @this {WebInspector.SASSSourceMapping}
- */
- function sourceMapLoaded(sourceMap)
- {
- if (!sourceMap)
- return;
-
- this._sourceMapByStyleSheetURL[sourceURL] = sourceMap;
- for (var i = 0; i < headersWithSameSourceURL.length; ++i) {
- if (forceRebind)
- WebInspector.cssWorkspaceBinding.updateLocations(headersWithSameSourceURL[i]);
- else
- this._bindUISourceCode(headersWithSameSourceURL[i], sourceMap);
+ for (var i = 0; i < cssURLs.length; ++i) {
+ var ids = this._cssModel.styleSheetIdsForURL(cssURLs[i]);
+ for (var j = 0; j < ids.length; ++j) {
+ var header = this._cssModel.styleSheetHeaderForId(ids[j]);
+ console.assert(header);
+ WebInspector.cssWorkspaceBinding.updateLocations(/** @type {!WebInspector.CSSStyleSheetHeader} */ (header));
}
}
},
/**
- * @param {string} completeSourceMapURL
- * @param {string} completeStyleSheetURL
- * @param {boolean} forceReload
- * @param {function(?WebInspector.SourceMap)} callback
+ * @param {!WebInspector.Event} event
*/
- _loadSourceMapForStyleSheet: function(completeSourceMapURL, completeStyleSheetURL, forceReload, callback)
+ _uiSourceCodeContentCommitted: function(event)
{
- var sourceMap = this._sourceMapByURL[completeSourceMapURL];
- if (sourceMap && !forceReload) {
- callback(sourceMap);
- return;
- }
-
- var pendingCallbacks = this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
- if (pendingCallbacks) {
- pendingCallbacks.push(callback);
- return;
+ var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
+ if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
+ var networkURL = this._networkMapping.networkURL(uiSourceCode);
+ var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray();
+ this._pollManager.sassFileChanged(networkURL, cssURLs, true);
}
+ },
- pendingCallbacks = [callback];
- this._pendingSourceMapLoadingCallbacks[completeSourceMapURL] = pendingCallbacks;
+ _reset: function()
+ {
+ this._addingRevisionCounter = 0;
+ this._completeSourceMapURLForCSSURL = {};
+ /** @type {!Multimap<string, string>} */
+ this._sassURLToCSSURLs = new Multimap();
+ /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>} */
+ this._pendingSourceMapLoadingCallbacks = {};
+ /** @type {!Object.<string, !WebInspector.SourceMap>} */
+ this._sourceMapByURL = {};
+ this._sourceMapByStyleSheetURL = {};
+ this._pollManager.reset();
+ }
+}
- WebInspector.SourceMap.load(completeSourceMapURL, completeStyleSheetURL, sourceMapLoaded.bind(this));
+/**
+ * @constructor
+ * @param {!WebInspector.CSSStyleModel} cssModel
+ * @param {!WebInspector.NetworkMapping} networkMapping
+ * @param {function(!WebInspector.UISourceCode, string):boolean} callback
+ */
+WebInspector.SASSSourceMapping.PollManager = function(cssModel, networkMapping, callback)
+{
+ this.pollPeriodMs = 30 * 1000;
+ this.pollIntervalMs = 200;
+ this._networkMapping = networkMapping;
+ this._callback = callback;
+ this._cssModel = cssModel;
+ this.reset();
+}
- /**
- * @param {?WebInspector.SourceMap} sourceMap
- * @this {WebInspector.SASSSourceMapping}
- */
- function sourceMapLoaded(sourceMap)
- {
- var callbacks = this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
- delete this._pendingSourceMapLoadingCallbacks[completeSourceMapURL];
- if (!callbacks)
- return;
- if (sourceMap)
- this._sourceMapByURL[completeSourceMapURL] = sourceMap;
- else
- delete this._sourceMapByURL[completeSourceMapURL];
- for (var i = 0; i < callbacks.length; ++i)
- callbacks[i](sourceMap);
- }
+WebInspector.SASSSourceMapping.PollManager.prototype = {
+ reset: function()
+ {
+ /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<string, !{timer: number, previousPoll: number}>}>} */
+ this._pollDataForSASSURL = {};
},
/**
- * @param {!WebInspector.CSSStyleSheetHeader} header
- * @param {!WebInspector.SourceMap} sourceMap
+ * @param {string} headerName
+ * @param {!Object.<string, string>} headers
+ * @return {?string}
*/
- _bindUISourceCode: function(header, sourceMap)
+ _headerValue: function(headerName, headers)
{
- WebInspector.cssWorkspaceBinding.pushSourceMapping(header, this);
- var cssURL = header.sourceURL;
- var sources = sourceMap.sources();
- for (var i = 0; i < sources.length; ++i) {
- var sassURL = sources[i];
- this._sassURLToCSSURLs.set(sassURL, cssURL);
- if (!this._networkMapping.hasMappingForURL(sassURL) && !this._networkMapping.uiSourceCodeForURL(sassURL, header.target())) {
- var contentProvider = sourceMap.sourceContentProvider(sassURL, WebInspector.resourceTypes.Stylesheet);
- this._networkProject.addFileForURL(sassURL, contentProvider);
+ headerName = headerName.toLowerCase();
+ var value = null;
+ for (var name in headers) {
+ if (name.toLowerCase() === headerName) {
+ value = headers[name];
+ break;
}
}
+ return value;
},
/**
- * @override
- * @param {!WebInspector.CSSLocation} rawLocation
- * @return {?WebInspector.UILocation}
+ * @param {!Object.<string, string>} headers
+ * @return {?Date}
*/
- rawLocationToUILocation: function(rawLocation)
+ _lastModified: function(headers)
{
- var sourceMap = this._sourceMapByStyleSheetURL[rawLocation.url];
- if (!sourceMap)
- return null;
- var entry = sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber);
- if (!entry || !entry.sourceURL)
+ var lastModifiedHeader = this._headerValue("last-modified", headers);
+ if (!lastModifiedHeader)
return null;
- var uiSourceCode = this._networkMapping.uiSourceCodeForURL(entry.sourceURL, rawLocation.target());
- if (!uiSourceCode)
+ var lastModified = new Date(lastModifiedHeader);
+ if (isNaN(lastModified.getTime()))
return null;
- return uiSourceCode.uiLocation(entry.sourceLineNumber, entry.sourceColumnNumber);
+ return lastModified;
},
/**
- * @override
- * @param {!WebInspector.UISourceCode} uiSourceCode
- * @param {number} lineNumber
- * @param {number} columnNumber
- * @return {?WebInspector.CSSLocation}
+ * @param {!Object.<string, string>} headers
+ * @param {string} url
+ * @return {?Date}
*/
- uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+ _checkLastModified: function(headers, url)
{
+ var lastModified = this._lastModified(headers);
+ if (lastModified)
+ return lastModified;
+
+ var etagMessage = this._headerValue("etag", headers) ? ", \"ETag\" response header found instead" : "";
+ var message = String.sprintf("The \"Last-Modified\" response header is missing or invalid for %s%s. The CSS auto-reload functionality will not work correctly.", url, etagMessage);
+ WebInspector.console.log(message);
return null;
},
/**
- * @override
- * @return {boolean}
+ * @param {string} sassURL
+ * @param {!Array.<string>} cssURLs
+ * @param {boolean} wasLoadedFromFileSystem
*/
- isIdentity: function()
+ sassFileChanged: function(sassURL, cssURLs, wasLoadedFromFileSystem)
{
- return false;
+ if (!cssURLs)
+ return;
+ if (!WebInspector.moduleSetting("cssReloadEnabled").get())
+ return;
+
+ var sassFile = this._networkMapping.uiSourceCodeForURL(sassURL, this._cssModel.target());
+ console.assert(sassFile);
+ if (wasLoadedFromFileSystem)
+ sassFile.requestMetadata(metadataReceived.bind(this));
+ else
+ WebInspector.ResourceLoader.loadUsingTargetUA(sassURL, null, sassLoadedViaNetwork.bind(this));
+
+ /**
+ * @param {number} statusCode
+ * @param {!Object.<string, string>} headers
+ * @param {string} content
+ * @this {WebInspector.SASSSourceMapping.PollManager}
+ */
+ function sassLoadedViaNetwork(statusCode, headers, content)
+ {
+ if (statusCode >= 400) {
+ console.error("Could not load content for " + sassURL + " : " + "HTTP status code: " + statusCode);
+ return;
+ }
+ var lastModified = this._checkLastModified(headers, sassURL);
+ if (!lastModified)
+ return;
+ metadataReceived.call(this, lastModified);
+ }
+
+ /**
+ * @param {?Date} timestamp
+ * @this {WebInspector.SASSSourceMapping.PollManager}
+ */
+ function metadataReceived(timestamp)
+ {
+ if (!timestamp)
+ return;
+
+ var now = Date.now();
+ var deadlineMs = now + this.pollPeriodMs;
+ var pollData = this._pollDataForSASSURL[sassURL];
+ if (pollData) {
+ var dataByURL = pollData.dataByURL;
+ for (var url in dataByURL)
+ clearTimeout(dataByURL[url].timer);
+ }
+ pollData = { dataByURL: {}, deadlineMs: deadlineMs, sassTimestamp: timestamp };
+ this._pollDataForSASSURL[sassURL] = pollData;
+ for (var i = 0; i < cssURLs.length; ++i) {
+ pollData.dataByURL[cssURLs[i]] = { previousPoll: now };
+ this._pollCallback(cssURLs[i], sassURL);
+ }
+ }
},
/**
- * @override
- * @param {!WebInspector.UISourceCode} uiSourceCode
- * @param {number} lineNumber
- * @return {boolean}
+ * @param {string} cssURL
+ * @param {string} sassURL
*/
- uiLineHasMapping: function(uiSourceCode, lineNumber)
+ _pollCallback: function(cssURL, sassURL)
{
- return true;
+ var now;
+ var pollData = this._pollDataForSASSURL[sassURL];
+ if (!pollData)
+ return;
+
+ if ((now = new Date().getTime()) > pollData.deadlineMs) {
+ WebInspector.console.warn(WebInspector.UIString("%s hasn't been updated in %d seconds.", cssURL, this.pollPeriodMs / 1000));
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+ var nextPoll = this.pollIntervalMs + pollData.dataByURL[cssURL].previousPoll;
+ var remainingTimeoutMs = Math.max(0, nextPoll - now);
+ pollData.dataByURL[cssURL].previousPoll = now + remainingTimeoutMs;
+ pollData.dataByURL[cssURL].timer = setTimeout(this._reloadCSS.bind(this, cssURL, sassURL), remainingTimeoutMs);
},
/**
- * @return {!WebInspector.Target}
+ * @param {string} cssURL
+ * @param {string} sassURL
*/
- target: function()
+ _stopPolling: function(cssURL, sassURL)
{
- return this._cssModel.target();
+ var pollData = this._pollDataForSASSURL[sassURL];
+ if (!pollData)
+ return;
+ delete pollData.dataByURL[cssURL];
+ if (!Object.keys(pollData.dataByURL).length)
+ delete this._pollDataForSASSURL[sassURL];
},
/**
- * @param {!WebInspector.Event} event
+ * @param {string} cssURL
+ * @param {string} sassURL
*/
- _uiSourceCodeAdded: function(event)
+ _reloadCSS: function(cssURL, sassURL)
{
- var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
- var networkURL = this._networkMapping.networkURL(uiSourceCode);
- var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray();
- if (!cssURLs)
+ var cssUISourceCode = this._networkMapping.uiSourceCodeForURL(cssURL, this._cssModel.target());
+ if (!cssUISourceCode) {
+ WebInspector.console.warn(WebInspector.UIString("%s resource missing. Please reload the page.", cssURL));
+ this._stopPolling(cssURL, sassURL)
return;
- for (var i = 0; i < cssURLs.length; ++i) {
- var ids = this._cssModel.styleSheetIdsForURL(cssURLs[i]);
- for (var j = 0; j < ids.length; ++j) {
- var header = this._cssModel.styleSheetHeaderForId(ids[j]);
- console.assert(header);
- WebInspector.cssWorkspaceBinding.updateLocations(/** @type {!WebInspector.CSSStyleSheetHeader} */ (header));
- }
}
+
+ if (this._networkMapping.hasMappingForURL(sassURL))
+ this._reloadCSSFromFileSystem(cssUISourceCode, sassURL);
+ else
+ this._reloadCSSFromNetwork(cssUISourceCode, sassURL);
},
/**
- * @param {!WebInspector.Event} event
+ * @param {!WebInspector.UISourceCode} cssUISourceCode
+ * @param {string} sassURL
*/
- _uiSourceCodeContentCommitted: function(event)
+ _reloadCSSFromNetwork: function(cssUISourceCode, sassURL)
{
- var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
- if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
- var networkURL = this._networkMapping.networkURL(uiSourceCode);
- var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray();
- this._sassFileSaved(networkURL, cssURLs, true);
+ var cssURL = this._networkMapping.networkURL(cssUISourceCode);
+ var data = this._pollDataForSASSURL[sassURL];
+ if (!data) {
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+ var headers = { "if-modified-since": new Date(data.sassTimestamp.getTime() - 1000).toUTCString() };
+ WebInspector.ResourceLoader.loadUsingTargetUA(cssURL, headers, contentLoaded.bind(this));
+
+ /**
+ * @param {number} statusCode
+ * @param {!Object.<string, string>} headers
+ * @param {string} content
+ * @this {WebInspector.SASSSourceMapping.PollManager}
+ */
+ function contentLoaded(statusCode, headers, content)
+ {
+ if (statusCode >= 400) {
+ console.error("Could not load content for " + cssURL + " : " + "HTTP status code: " + statusCode);
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+ if (!this._pollDataForSASSURL[sassURL]) {
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+ if (statusCode === 304) {
+ this._pollCallback(cssURL, sassURL);
+ return;
+ }
+ var lastModified = this._checkLastModified(headers, cssURL);
+ if (!lastModified) {
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+ if (lastModified.getTime() < data.sassTimestamp.getTime()) {
+ this._pollCallback(cssURL, sassURL);
+ return;
+ }
+ if (this._callback(cssUISourceCode, content))
+ this._stopPolling(cssURL, sassURL);
}
},
- _reset: function()
+ /**
+ * @param {!WebInspector.UISourceCode} cssUISourceCode
+ * @param {string} sassURL
+ */
+ _reloadCSSFromFileSystem: function(cssUISourceCode, sassURL)
{
- this._addingRevisionCounter = 0;
- this._completeSourceMapURLForCSSURL = {};
- /** @type {!Multimap<string, string>} */
- this._sassURLToCSSURLs = new Multimap();
- /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>} */
- this._pendingSourceMapLoadingCallbacks = {};
- /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<string, !{timer: number, previousPoll: number}>}>} */
- this._pollDataForSASSURL = {};
- /** @type {!Object.<string, !WebInspector.SourceMap>} */
- this._sourceMapByURL = {};
- this._sourceMapByStyleSheetURL = {};
+ cssUISourceCode.requestMetadata(metadataCallback.bind(this));
+
+ /**
+ * @param {?Date} timestamp
+ * @this {WebInspector.SASSSourceMapping.PollManager}
+ */
+ function metadataCallback(timestamp)
+ {
+ var cssURL = this._networkMapping.networkURL(cssUISourceCode);
+ if (!timestamp) {
+ this._pollCallback(cssURL, sassURL);
+ return;
+ }
+ var cssTimestamp = timestamp.getTime();
+ var pollData = this._pollDataForSASSURL[sassURL];
+ if (!pollData) {
+ this._stopPolling(cssURL, sassURL);
+ return;
+ }
+
+ if (cssTimestamp < pollData.sassTimestamp.getTime()) {
+ this._pollCallback(cssURL, sassURL);
+ return;
+ }
+
+ cssUISourceCode.requestOriginalContent(contentCallback.bind(this));
+
+ /**
+ * @param {?string} content
+ * @this {WebInspector.SASSSourceMapping.PollManager}
+ */
+ function contentCallback(content)
+ {
+ // Empty string is a valid value, null means error.
+ if (content === null)
+ return;
+ if (this._callback(cssUISourceCode, content))
+ this._stopPolling(cssURL, sassURL);
+ }
+ }
}
-}
+}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698