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 |