| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 /** | 31 /** |
| 32 * @constructor | 32 * @constructor |
| 33 * @implements {WebInspector.CSSSourceMapping} | 33 * @implements {WebInspector.CSSSourceMapping} |
| 34 * @param {!WebInspector.CSSStyleModel} cssModel | 34 * @param {!WebInspector.CSSStyleModel} cssModel |
| 35 * @param {!WebInspector.Workspace} workspace | 35 * @param {!WebInspector.Workspace} workspace |
| 36 * @param {!WebInspector.NetworkMapping} networkMapping | 36 * @param {!WebInspector.NetworkMapping} networkMapping |
| 37 * @param {!WebInspector.NetworkProject} networkProject | 37 * @param {!WebInspector.NetworkProject} networkProject |
| 38 */ | 38 */ |
| 39 WebInspector.SASSSourceMapping = function(cssModel, workspace, networkMapping, n
etworkProject) | 39 WebInspector.SASSSourceMapping = function(cssModel, workspace, networkMapping, n
etworkProject) |
| 40 { | 40 { |
| 41 this.pollPeriodMs = 30 * 1000; | |
| 42 this.pollIntervalMs = 200; | |
| 43 this._cssModel = cssModel; | 41 this._cssModel = cssModel; |
| 44 this._workspace = workspace; | 42 this._workspace = workspace; |
| 45 this._networkProject = networkProject; | 43 this._networkProject = networkProject; |
| 46 this._addingRevisionCounter = 0; | 44 this._addingRevisionCounter = 0; |
| 45 this._pollManager = new WebInspector.SASSSourceMapping.PollManager(this._css
Model, networkMapping, this._updateCSSRevision.bind(this)); |
| 47 this._reset(); | 46 this._reset(); |
| 48 WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventType
s.SavedURL, this._fileSaveFinished, this); | 47 WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventType
s.SavedURL, this._fileSaveFinished, this); |
| 49 WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._t
oggleSourceMapSupport, this); | 48 WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._t
oggleSourceMapSupport, this); |
| 50 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheet
Changed, this._styleSheetChanged, this); | 49 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheet
Changed, this._styleSheetChanged, this); |
| 51 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAdded, this); | 50 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAdded, this); |
| 52 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeC
ontentCommitted, this._uiSourceCodeContentCommitted, this); | 51 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeC
ontentCommitted, this._uiSourceCodeContentCommitted, this); |
| 53 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemove
d, this._reset, this); | 52 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemove
d, this._reset, this); |
| 54 this._networkMapping = networkMapping; | 53 this._networkMapping = networkMapping; |
| 55 } | 54 } |
| 56 | 55 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 87 } | 86 } |
| 88 }, | 87 }, |
| 89 | 88 |
| 90 /** | 89 /** |
| 91 * @param {!WebInspector.Event} event | 90 * @param {!WebInspector.Event} event |
| 92 */ | 91 */ |
| 93 _fileSaveFinished: function(event) | 92 _fileSaveFinished: function(event) |
| 94 { | 93 { |
| 95 var sassURL = /** @type {string} */ (event.data); | 94 var sassURL = /** @type {string} */ (event.data); |
| 96 var cssURLs = this._sassURLToCSSURLs.get(sassURL).valuesArray(); | 95 var cssURLs = this._sassURLToCSSURLs.get(sassURL).valuesArray(); |
| 97 this._sassFileSaved(sassURL, cssURLs, false); | 96 this._pollManager.sassFileChanged(sassURL, cssURLs, false); |
| 98 }, | |
| 99 | |
| 100 /** | |
| 101 * @param {string} headerName | |
| 102 * @param {!Object.<string, string>} headers | |
| 103 * @return {?string} | |
| 104 */ | |
| 105 _headerValue: function(headerName, headers) | |
| 106 { | |
| 107 headerName = headerName.toLowerCase(); | |
| 108 var value = null; | |
| 109 for (var name in headers) { | |
| 110 if (name.toLowerCase() === headerName) { | |
| 111 value = headers[name]; | |
| 112 break; | |
| 113 } | |
| 114 } | |
| 115 return value; | |
| 116 }, | |
| 117 | |
| 118 /** | |
| 119 * @param {!Object.<string, string>} headers | |
| 120 * @return {?Date} | |
| 121 */ | |
| 122 _lastModified: function(headers) | |
| 123 { | |
| 124 var lastModifiedHeader = this._headerValue("last-modified", headers); | |
| 125 if (!lastModifiedHeader) | |
| 126 return null; | |
| 127 var lastModified = new Date(lastModifiedHeader); | |
| 128 if (isNaN(lastModified.getTime())) | |
| 129 return null; | |
| 130 return lastModified; | |
| 131 }, | |
| 132 | |
| 133 /** | |
| 134 * @param {!Object.<string, string>} headers | |
| 135 * @param {string} url | |
| 136 * @return {?Date} | |
| 137 */ | |
| 138 _checkLastModified: function(headers, url) | |
| 139 { | |
| 140 var lastModified = this._lastModified(headers); | |
| 141 if (lastModified) | |
| 142 return lastModified; | |
| 143 | |
| 144 var etagMessage = this._headerValue("etag", headers) ? ", \"ETag\" respo
nse header found instead" : ""; | |
| 145 var message = String.sprintf("The \"Last-Modified\" response header is m
issing or invalid for %s%s. The CSS auto-reload functionality will not work corr
ectly.", url, etagMessage); | |
| 146 WebInspector.console.log(message); | |
| 147 return null; | |
| 148 }, | |
| 149 | |
| 150 /** | |
| 151 * @param {string} sassURL | |
| 152 * @param {!Array.<string>} cssURLs | |
| 153 * @param {boolean} wasLoadedFromFileSystem | |
| 154 */ | |
| 155 _sassFileSaved: function(sassURL, cssURLs, wasLoadedFromFileSystem) | |
| 156 { | |
| 157 if (!cssURLs) | |
| 158 return; | |
| 159 if (!WebInspector.moduleSetting("cssReloadEnabled").get()) | |
| 160 return; | |
| 161 | |
| 162 var sassFile = this._networkMapping.uiSourceCodeForURL(sassURL, this._cs
sModel.target()); | |
| 163 console.assert(sassFile); | |
| 164 if (wasLoadedFromFileSystem) | |
| 165 sassFile.requestMetadata(metadataReceived.bind(this)); | |
| 166 else | |
| 167 WebInspector.ResourceLoader.loadUsingTargetUA(sassURL, null, sassLoa
dedViaNetwork.bind(this)); | |
| 168 | |
| 169 /** | |
| 170 * @param {number} statusCode | |
| 171 * @param {!Object.<string, string>} headers | |
| 172 * @param {string} content | |
| 173 * @this {WebInspector.SASSSourceMapping} | |
| 174 */ | |
| 175 function sassLoadedViaNetwork(statusCode, headers, content) | |
| 176 { | |
| 177 if (statusCode >= 400) { | |
| 178 console.error("Could not load content for " + sassURL + " : " +
"HTTP status code: " + statusCode); | |
| 179 return; | |
| 180 } | |
| 181 var lastModified = this._checkLastModified(headers, sassURL); | |
| 182 if (!lastModified) | |
| 183 return; | |
| 184 metadataReceived.call(this, lastModified); | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * @param {?Date} timestamp | |
| 189 * @this {WebInspector.SASSSourceMapping} | |
| 190 */ | |
| 191 function metadataReceived(timestamp) | |
| 192 { | |
| 193 if (!timestamp) | |
| 194 return; | |
| 195 | |
| 196 var now = Date.now(); | |
| 197 var deadlineMs = now + this.pollPeriodMs; | |
| 198 var pollData = this._pollDataForSASSURL[sassURL]; | |
| 199 if (pollData) { | |
| 200 var dataByURL = pollData.dataByURL; | |
| 201 for (var url in dataByURL) | |
| 202 clearTimeout(dataByURL[url].timer); | |
| 203 } | |
| 204 pollData = { dataByURL: {}, deadlineMs: deadlineMs, sassTimestamp: t
imestamp }; | |
| 205 this._pollDataForSASSURL[sassURL] = pollData; | |
| 206 for (var i = 0; i < cssURLs.length; ++i) { | |
| 207 pollData.dataByURL[cssURLs[i]] = { previousPoll: now }; | |
| 208 this._pollCallback(cssURLs[i], sassURL); | |
| 209 } | |
| 210 } | |
| 211 }, | |
| 212 | |
| 213 /** | |
| 214 * @param {string} cssURL | |
| 215 * @param {string} sassURL | |
| 216 */ | |
| 217 _pollCallback: function(cssURL, sassURL) | |
| 218 { | |
| 219 var now; | |
| 220 var pollData = this._pollDataForSASSURL[sassURL]; | |
| 221 if (!pollData) | |
| 222 return; | |
| 223 | |
| 224 if ((now = new Date().getTime()) > pollData.deadlineMs) { | |
| 225 WebInspector.console.warn(WebInspector.UIString("%s hasn't been upda
ted in %d seconds.", cssURL, this.pollPeriodMs / 1000)); | |
| 226 this._stopPolling(cssURL, sassURL); | |
| 227 return; | |
| 228 } | |
| 229 var nextPoll = this.pollIntervalMs + pollData.dataByURL[cssURL].previous
Poll; | |
| 230 var remainingTimeoutMs = Math.max(0, nextPoll - now); | |
| 231 pollData.dataByURL[cssURL].previousPoll = now + remainingTimeoutMs; | |
| 232 pollData.dataByURL[cssURL].timer = setTimeout(this._reloadCSS.bind(this,
cssURL, sassURL), remainingTimeoutMs); | |
| 233 }, | |
| 234 | |
| 235 /** | |
| 236 * @param {string} cssURL | |
| 237 * @param {string} sassURL | |
| 238 */ | |
| 239 _stopPolling: function(cssURL, sassURL) | |
| 240 { | |
| 241 var pollData = this._pollDataForSASSURL[sassURL]; | |
| 242 if (!pollData) | |
| 243 return; | |
| 244 delete pollData.dataByURL[cssURL]; | |
| 245 if (!Object.keys(pollData.dataByURL).length) | |
| 246 delete this._pollDataForSASSURL[sassURL]; | |
| 247 }, | |
| 248 | |
| 249 /** | |
| 250 * @param {string} cssURL | |
| 251 * @param {string} sassURL | |
| 252 */ | |
| 253 _reloadCSS: function(cssURL, sassURL) | |
| 254 { | |
| 255 var cssUISourceCode = this._networkMapping.uiSourceCodeForURL(cssURL, th
is._cssModel.target()); | |
| 256 if (!cssUISourceCode) { | |
| 257 WebInspector.console.warn(WebInspector.UIString("%s resource missing
. Please reload the page.", cssURL)); | |
| 258 this._stopPolling(cssURL, sassURL) | |
| 259 return; | |
| 260 } | |
| 261 | |
| 262 if (this._networkMapping.hasMappingForURL(sassURL)) | |
| 263 this._reloadCSSFromFileSystem(cssUISourceCode, sassURL); | |
| 264 else | |
| 265 this._reloadCSSFromNetwork(cssUISourceCode, sassURL); | |
| 266 }, | |
| 267 | |
| 268 /** | |
| 269 * @param {!WebInspector.UISourceCode} cssUISourceCode | |
| 270 * @param {string} sassURL | |
| 271 */ | |
| 272 _reloadCSSFromNetwork: function(cssUISourceCode, sassURL) | |
| 273 { | |
| 274 var cssURL = this._networkMapping.networkURL(cssUISourceCode); | |
| 275 var data = this._pollDataForSASSURL[sassURL]; | |
| 276 if (!data) { | |
| 277 this._stopPolling(cssURL, sassURL); | |
| 278 return; | |
| 279 } | |
| 280 var headers = { "if-modified-since": new Date(data.sassTimestamp.getTime
() - 1000).toUTCString() }; | |
| 281 WebInspector.ResourceLoader.loadUsingTargetUA(cssURL, headers, contentLo
aded.bind(this)); | |
| 282 | |
| 283 /** | |
| 284 * @param {number} statusCode | |
| 285 * @param {!Object.<string, string>} headers | |
| 286 * @param {string} content | |
| 287 * @this {WebInspector.SASSSourceMapping} | |
| 288 */ | |
| 289 function contentLoaded(statusCode, headers, content) | |
| 290 { | |
| 291 if (statusCode >= 400) { | |
| 292 console.error("Could not load content for " + cssURL + " : " + "
HTTP status code: " + statusCode); | |
| 293 this._stopPolling(cssURL, sassURL); | |
| 294 return; | |
| 295 } | |
| 296 if (!this._pollDataForSASSURL[sassURL]) { | |
| 297 this._stopPolling(cssURL, sassURL); | |
| 298 return; | |
| 299 } | |
| 300 if (statusCode === 304) { | |
| 301 this._pollCallback(cssURL, sassURL); | |
| 302 return; | |
| 303 } | |
| 304 var lastModified = this._checkLastModified(headers, cssURL); | |
| 305 if (!lastModified) { | |
| 306 this._stopPolling(cssURL, sassURL); | |
| 307 return; | |
| 308 } | |
| 309 if (lastModified.getTime() < data.sassTimestamp.getTime()) { | |
| 310 this._pollCallback(cssURL, sassURL); | |
| 311 return; | |
| 312 } | |
| 313 if (this._updateCSSRevision(cssUISourceCode, content, sassURL)) | |
| 314 this._stopPolling(cssURL, sassURL); | |
| 315 } | |
| 316 }, | |
| 317 | |
| 318 /** | |
| 319 * @param {!WebInspector.UISourceCode} cssUISourceCode | |
| 320 * @param {string} sassURL | |
| 321 */ | |
| 322 _reloadCSSFromFileSystem: function(cssUISourceCode, sassURL) | |
| 323 { | |
| 324 cssUISourceCode.requestMetadata(metadataCallback.bind(this)); | |
| 325 | |
| 326 /** | |
| 327 * @param {?Date} timestamp | |
| 328 * @this {WebInspector.SASSSourceMapping} | |
| 329 */ | |
| 330 function metadataCallback(timestamp) | |
| 331 { | |
| 332 var cssURL = this._networkMapping.networkURL(cssUISourceCode); | |
| 333 if (!timestamp) { | |
| 334 this._pollCallback(cssURL, sassURL); | |
| 335 return; | |
| 336 } | |
| 337 var cssTimestamp = timestamp.getTime(); | |
| 338 var pollData = this._pollDataForSASSURL[sassURL]; | |
| 339 if (!pollData) { | |
| 340 this._stopPolling(cssURL, sassURL); | |
| 341 return; | |
| 342 } | |
| 343 | |
| 344 if (cssTimestamp < pollData.sassTimestamp.getTime()) { | |
| 345 this._pollCallback(cssURL, sassURL); | |
| 346 return; | |
| 347 } | |
| 348 | |
| 349 cssUISourceCode.requestOriginalContent(contentCallback.bind(this)); | |
| 350 | |
| 351 /** | |
| 352 * @param {?string} content | |
| 353 * @this {WebInspector.SASSSourceMapping} | |
| 354 */ | |
| 355 function contentCallback(content) | |
| 356 { | |
| 357 // Empty string is a valid value, null means error. | |
| 358 if (content === null) | |
| 359 return; | |
| 360 if (this._updateCSSRevision(cssUISourceCode, content, sassURL)) | |
| 361 this._stopPolling(cssURL, sassURL); | |
| 362 } | |
| 363 } | |
| 364 }, | 97 }, |
| 365 | 98 |
| 366 /** | 99 /** |
| 367 * @param {!WebInspector.UISourceCode} cssUISourceCode | 100 * @param {!WebInspector.UISourceCode} cssUISourceCode |
| 368 * @param {string} content | 101 * @param {string} content |
| 369 * @param {string} sassURL | |
| 370 * @return {boolean} | 102 * @return {boolean} |
| 371 */ | 103 */ |
| 372 _updateCSSRevision: function(cssUISourceCode, content, sassURL) | 104 _updateCSSRevision: function(cssUISourceCode, content) |
| 373 { | 105 { |
| 374 ++this._addingRevisionCounter; | 106 ++this._addingRevisionCounter; |
| 375 cssUISourceCode.addRevision(content); | 107 cssUISourceCode.addRevision(content); |
| 376 var cssURL = this._networkMapping.networkURL(cssUISourceCode); | 108 var cssURL = this._networkMapping.networkURL(cssUISourceCode); |
| 377 var completeSourceMapURL = this._completeSourceMapURLForCSSURL[cssURL]; | 109 var completeSourceMapURL = this._completeSourceMapURLForCSSURL[cssURL]; |
| 378 if (!completeSourceMapURL) | 110 if (!completeSourceMapURL) |
| 379 return false; | 111 return false; |
| 380 var ids = this._cssModel.styleSheetIdsForURL(cssURL); | 112 var ids = this._cssModel.styleSheetIdsForURL(cssURL); |
| 381 if (!ids) | 113 if (!ids) |
| 382 return false; | 114 return false; |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 | 328 |
| 597 /** | 329 /** |
| 598 * @param {!WebInspector.Event} event | 330 * @param {!WebInspector.Event} event |
| 599 */ | 331 */ |
| 600 _uiSourceCodeContentCommitted: function(event) | 332 _uiSourceCodeContentCommitted: function(event) |
| 601 { | 333 { |
| 602 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
.uiSourceCode); | 334 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
.uiSourceCode); |
| 603 if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSyst
em) { | 335 if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSyst
em) { |
| 604 var networkURL = this._networkMapping.networkURL(uiSourceCode); | 336 var networkURL = this._networkMapping.networkURL(uiSourceCode); |
| 605 var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray(); | 337 var cssURLs = this._sassURLToCSSURLs.get(networkURL).valuesArray(); |
| 606 this._sassFileSaved(networkURL, cssURLs, true); | 338 this._pollManager.sassFileChanged(networkURL, cssURLs, true); |
| 607 } | 339 } |
| 608 }, | 340 }, |
| 609 | 341 |
| 610 _reset: function() | 342 _reset: function() |
| 611 { | 343 { |
| 612 this._addingRevisionCounter = 0; | 344 this._addingRevisionCounter = 0; |
| 613 this._completeSourceMapURLForCSSURL = {}; | 345 this._completeSourceMapURLForCSSURL = {}; |
| 614 /** @type {!Multimap<string, string>} */ | 346 /** @type {!Multimap<string, string>} */ |
| 615 this._sassURLToCSSURLs = new Multimap(); | 347 this._sassURLToCSSURLs = new Multimap(); |
| 616 /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>}
*/ | 348 /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>}
*/ |
| 617 this._pendingSourceMapLoadingCallbacks = {}; | 349 this._pendingSourceMapLoadingCallbacks = {}; |
| 618 /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<st
ring, !{timer: number, previousPoll: number}>}>} */ | |
| 619 this._pollDataForSASSURL = {}; | |
| 620 /** @type {!Object.<string, !WebInspector.SourceMap>} */ | 350 /** @type {!Object.<string, !WebInspector.SourceMap>} */ |
| 621 this._sourceMapByURL = {}; | 351 this._sourceMapByURL = {}; |
| 622 this._sourceMapByStyleSheetURL = {}; | 352 this._sourceMapByStyleSheetURL = {}; |
| 353 this._pollManager.reset(); |
| 623 } | 354 } |
| 624 } | 355 } |
| 356 |
| 357 /** |
| 358 * @constructor |
| 359 * @param {!WebInspector.CSSStyleModel} cssModel |
| 360 * @param {!WebInspector.NetworkMapping} networkMapping |
| 361 * @param {function(!WebInspector.UISourceCode, string):boolean} callback |
| 362 */ |
| 363 WebInspector.SASSSourceMapping.PollManager = function(cssModel, networkMapping,
callback) |
| 364 { |
| 365 this.pollPeriodMs = 30 * 1000; |
| 366 this.pollIntervalMs = 200; |
| 367 this._networkMapping = networkMapping; |
| 368 this._callback = callback; |
| 369 this._cssModel = cssModel; |
| 370 this.reset(); |
| 371 } |
| 372 |
| 373 WebInspector.SASSSourceMapping.PollManager.prototype = { |
| 374 reset: function() |
| 375 { |
| 376 /** @type {!Object.<string, !{deadlineMs: number, dataByURL: !Object.<st
ring, !{timer: number, previousPoll: number}>}>} */ |
| 377 this._pollDataForSASSURL = {}; |
| 378 }, |
| 379 |
| 380 /** |
| 381 * @param {string} headerName |
| 382 * @param {!Object.<string, string>} headers |
| 383 * @return {?string} |
| 384 */ |
| 385 _headerValue: function(headerName, headers) |
| 386 { |
| 387 headerName = headerName.toLowerCase(); |
| 388 var value = null; |
| 389 for (var name in headers) { |
| 390 if (name.toLowerCase() === headerName) { |
| 391 value = headers[name]; |
| 392 break; |
| 393 } |
| 394 } |
| 395 return value; |
| 396 }, |
| 397 |
| 398 /** |
| 399 * @param {!Object.<string, string>} headers |
| 400 * @return {?Date} |
| 401 */ |
| 402 _lastModified: function(headers) |
| 403 { |
| 404 var lastModifiedHeader = this._headerValue("last-modified", headers); |
| 405 if (!lastModifiedHeader) |
| 406 return null; |
| 407 var lastModified = new Date(lastModifiedHeader); |
| 408 if (isNaN(lastModified.getTime())) |
| 409 return null; |
| 410 return lastModified; |
| 411 }, |
| 412 |
| 413 /** |
| 414 * @param {!Object.<string, string>} headers |
| 415 * @param {string} url |
| 416 * @return {?Date} |
| 417 */ |
| 418 _checkLastModified: function(headers, url) |
| 419 { |
| 420 var lastModified = this._lastModified(headers); |
| 421 if (lastModified) |
| 422 return lastModified; |
| 423 |
| 424 var etagMessage = this._headerValue("etag", headers) ? ", \"ETag\" respo
nse header found instead" : ""; |
| 425 var message = String.sprintf("The \"Last-Modified\" response header is m
issing or invalid for %s%s. The CSS auto-reload functionality will not work corr
ectly.", url, etagMessage); |
| 426 WebInspector.console.log(message); |
| 427 return null; |
| 428 }, |
| 429 |
| 430 /** |
| 431 * @param {string} sassURL |
| 432 * @param {!Array.<string>} cssURLs |
| 433 * @param {boolean} wasLoadedFromFileSystem |
| 434 */ |
| 435 sassFileChanged: function(sassURL, cssURLs, wasLoadedFromFileSystem) |
| 436 { |
| 437 if (!cssURLs) |
| 438 return; |
| 439 if (!WebInspector.moduleSetting("cssReloadEnabled").get()) |
| 440 return; |
| 441 |
| 442 var sassFile = this._networkMapping.uiSourceCodeForURL(sassURL, this._cs
sModel.target()); |
| 443 console.assert(sassFile); |
| 444 if (wasLoadedFromFileSystem) |
| 445 sassFile.requestMetadata(metadataReceived.bind(this)); |
| 446 else |
| 447 WebInspector.ResourceLoader.loadUsingTargetUA(sassURL, null, sassLoa
dedViaNetwork.bind(this)); |
| 448 |
| 449 /** |
| 450 * @param {number} statusCode |
| 451 * @param {!Object.<string, string>} headers |
| 452 * @param {string} content |
| 453 * @this {WebInspector.SASSSourceMapping.PollManager} |
| 454 */ |
| 455 function sassLoadedViaNetwork(statusCode, headers, content) |
| 456 { |
| 457 if (statusCode >= 400) { |
| 458 console.error("Could not load content for " + sassURL + " : " +
"HTTP status code: " + statusCode); |
| 459 return; |
| 460 } |
| 461 var lastModified = this._checkLastModified(headers, sassURL); |
| 462 if (!lastModified) |
| 463 return; |
| 464 metadataReceived.call(this, lastModified); |
| 465 } |
| 466 |
| 467 /** |
| 468 * @param {?Date} timestamp |
| 469 * @this {WebInspector.SASSSourceMapping.PollManager} |
| 470 */ |
| 471 function metadataReceived(timestamp) |
| 472 { |
| 473 if (!timestamp) |
| 474 return; |
| 475 |
| 476 var now = Date.now(); |
| 477 var deadlineMs = now + this.pollPeriodMs; |
| 478 var pollData = this._pollDataForSASSURL[sassURL]; |
| 479 if (pollData) { |
| 480 var dataByURL = pollData.dataByURL; |
| 481 for (var url in dataByURL) |
| 482 clearTimeout(dataByURL[url].timer); |
| 483 } |
| 484 pollData = { dataByURL: {}, deadlineMs: deadlineMs, sassTimestamp: t
imestamp }; |
| 485 this._pollDataForSASSURL[sassURL] = pollData; |
| 486 for (var i = 0; i < cssURLs.length; ++i) { |
| 487 pollData.dataByURL[cssURLs[i]] = { previousPoll: now }; |
| 488 this._pollCallback(cssURLs[i], sassURL); |
| 489 } |
| 490 } |
| 491 }, |
| 492 |
| 493 /** |
| 494 * @param {string} cssURL |
| 495 * @param {string} sassURL |
| 496 */ |
| 497 _pollCallback: function(cssURL, sassURL) |
| 498 { |
| 499 var now; |
| 500 var pollData = this._pollDataForSASSURL[sassURL]; |
| 501 if (!pollData) |
| 502 return; |
| 503 |
| 504 if ((now = new Date().getTime()) > pollData.deadlineMs) { |
| 505 WebInspector.console.warn(WebInspector.UIString("%s hasn't been upda
ted in %d seconds.", cssURL, this.pollPeriodMs / 1000)); |
| 506 this._stopPolling(cssURL, sassURL); |
| 507 return; |
| 508 } |
| 509 var nextPoll = this.pollIntervalMs + pollData.dataByURL[cssURL].previous
Poll; |
| 510 var remainingTimeoutMs = Math.max(0, nextPoll - now); |
| 511 pollData.dataByURL[cssURL].previousPoll = now + remainingTimeoutMs; |
| 512 pollData.dataByURL[cssURL].timer = setTimeout(this._reloadCSS.bind(this,
cssURL, sassURL), remainingTimeoutMs); |
| 513 }, |
| 514 |
| 515 /** |
| 516 * @param {string} cssURL |
| 517 * @param {string} sassURL |
| 518 */ |
| 519 _stopPolling: function(cssURL, sassURL) |
| 520 { |
| 521 var pollData = this._pollDataForSASSURL[sassURL]; |
| 522 if (!pollData) |
| 523 return; |
| 524 delete pollData.dataByURL[cssURL]; |
| 525 if (!Object.keys(pollData.dataByURL).length) |
| 526 delete this._pollDataForSASSURL[sassURL]; |
| 527 }, |
| 528 |
| 529 /** |
| 530 * @param {string} cssURL |
| 531 * @param {string} sassURL |
| 532 */ |
| 533 _reloadCSS: function(cssURL, sassURL) |
| 534 { |
| 535 var cssUISourceCode = this._networkMapping.uiSourceCodeForURL(cssURL, th
is._cssModel.target()); |
| 536 if (!cssUISourceCode) { |
| 537 WebInspector.console.warn(WebInspector.UIString("%s resource missing
. Please reload the page.", cssURL)); |
| 538 this._stopPolling(cssURL, sassURL) |
| 539 return; |
| 540 } |
| 541 |
| 542 if (this._networkMapping.hasMappingForURL(sassURL)) |
| 543 this._reloadCSSFromFileSystem(cssUISourceCode, sassURL); |
| 544 else |
| 545 this._reloadCSSFromNetwork(cssUISourceCode, sassURL); |
| 546 }, |
| 547 |
| 548 /** |
| 549 * @param {!WebInspector.UISourceCode} cssUISourceCode |
| 550 * @param {string} sassURL |
| 551 */ |
| 552 _reloadCSSFromNetwork: function(cssUISourceCode, sassURL) |
| 553 { |
| 554 var cssURL = this._networkMapping.networkURL(cssUISourceCode); |
| 555 var data = this._pollDataForSASSURL[sassURL]; |
| 556 if (!data) { |
| 557 this._stopPolling(cssURL, sassURL); |
| 558 return; |
| 559 } |
| 560 var headers = { "if-modified-since": new Date(data.sassTimestamp.getTime
() - 1000).toUTCString() }; |
| 561 WebInspector.ResourceLoader.loadUsingTargetUA(cssURL, headers, contentLo
aded.bind(this)); |
| 562 |
| 563 /** |
| 564 * @param {number} statusCode |
| 565 * @param {!Object.<string, string>} headers |
| 566 * @param {string} content |
| 567 * @this {WebInspector.SASSSourceMapping.PollManager} |
| 568 */ |
| 569 function contentLoaded(statusCode, headers, content) |
| 570 { |
| 571 if (statusCode >= 400) { |
| 572 console.error("Could not load content for " + cssURL + " : " + "
HTTP status code: " + statusCode); |
| 573 this._stopPolling(cssURL, sassURL); |
| 574 return; |
| 575 } |
| 576 if (!this._pollDataForSASSURL[sassURL]) { |
| 577 this._stopPolling(cssURL, sassURL); |
| 578 return; |
| 579 } |
| 580 if (statusCode === 304) { |
| 581 this._pollCallback(cssURL, sassURL); |
| 582 return; |
| 583 } |
| 584 var lastModified = this._checkLastModified(headers, cssURL); |
| 585 if (!lastModified) { |
| 586 this._stopPolling(cssURL, sassURL); |
| 587 return; |
| 588 } |
| 589 if (lastModified.getTime() < data.sassTimestamp.getTime()) { |
| 590 this._pollCallback(cssURL, sassURL); |
| 591 return; |
| 592 } |
| 593 if (this._callback(cssUISourceCode, content)) |
| 594 this._stopPolling(cssURL, sassURL); |
| 595 } |
| 596 }, |
| 597 |
| 598 /** |
| 599 * @param {!WebInspector.UISourceCode} cssUISourceCode |
| 600 * @param {string} sassURL |
| 601 */ |
| 602 _reloadCSSFromFileSystem: function(cssUISourceCode, sassURL) |
| 603 { |
| 604 cssUISourceCode.requestMetadata(metadataCallback.bind(this)); |
| 605 |
| 606 /** |
| 607 * @param {?Date} timestamp |
| 608 * @this {WebInspector.SASSSourceMapping.PollManager} |
| 609 */ |
| 610 function metadataCallback(timestamp) |
| 611 { |
| 612 var cssURL = this._networkMapping.networkURL(cssUISourceCode); |
| 613 if (!timestamp) { |
| 614 this._pollCallback(cssURL, sassURL); |
| 615 return; |
| 616 } |
| 617 var cssTimestamp = timestamp.getTime(); |
| 618 var pollData = this._pollDataForSASSURL[sassURL]; |
| 619 if (!pollData) { |
| 620 this._stopPolling(cssURL, sassURL); |
| 621 return; |
| 622 } |
| 623 |
| 624 if (cssTimestamp < pollData.sassTimestamp.getTime()) { |
| 625 this._pollCallback(cssURL, sassURL); |
| 626 return; |
| 627 } |
| 628 |
| 629 cssUISourceCode.requestOriginalContent(contentCallback.bind(this)); |
| 630 |
| 631 /** |
| 632 * @param {?string} content |
| 633 * @this {WebInspector.SASSSourceMapping.PollManager} |
| 634 */ |
| 635 function contentCallback(content) |
| 636 { |
| 637 // Empty string is a valid value, null means error. |
| 638 if (content === null) |
| 639 return; |
| 640 if (this._callback(cssUISourceCode, content)) |
| 641 this._stopPolling(cssURL, sassURL); |
| 642 } |
| 643 } |
| 644 } |
| 645 } |
| OLD | NEW |