| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 /** | |
| 32 * @constructor | |
| 33 * @implements {WebInspector.SourceMapping} | |
| 34 * @param {!WebInspector.CSSStyleModel} cssModel | |
| 35 * @param {!WebInspector.Workspace} workspace | |
| 36 */ | |
| 37 WebInspector.StylesSourceMapping = function(cssModel, workspace) | |
| 38 { | |
| 39 this._cssModel = cssModel; | |
| 40 this._workspace = workspace; | |
| 41 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemove
d, this._projectRemoved, this); | |
| 42 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeA
dded, this._uiSourceCodeAddedToWorkspace, this); | |
| 43 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeR
emoved, this._uiSourceCodeRemoved, this); | |
| 44 | |
| 45 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeMod
el.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this); | |
| 46 | |
| 47 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheet
Changed, this._styleSheetChanged, this); | |
| 48 this._initialize(); | |
| 49 } | |
| 50 | |
| 51 WebInspector.StylesSourceMapping.MinorChangeUpdateTimeoutMs = 1000; | |
| 52 | |
| 53 WebInspector.StylesSourceMapping.prototype = { | |
| 54 /** | |
| 55 * @param {!WebInspector.RawLocation} rawLocation | |
| 56 * @return {?WebInspector.UILocation} | |
| 57 */ | |
| 58 rawLocationToUILocation: function(rawLocation) | |
| 59 { | |
| 60 var location = /** @type WebInspector.CSSLocation */ (rawLocation); | |
| 61 var uiSourceCode = this._workspace.uiSourceCodeForURL(location.url); | |
| 62 if (!uiSourceCode) | |
| 63 return null; | |
| 64 return uiSourceCode.uiLocation(location.lineNumber, location.columnNumbe
r); | |
| 65 }, | |
| 66 | |
| 67 /** | |
| 68 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 69 * @param {number} lineNumber | |
| 70 * @param {number} columnNumber | |
| 71 * @return {!WebInspector.RawLocation} | |
| 72 */ | |
| 73 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) | |
| 74 { | |
| 75 return new WebInspector.CSSLocation(this._cssModel.target(), null, uiSou
rceCode.url || "", lineNumber, columnNumber); | |
| 76 }, | |
| 77 | |
| 78 /** | |
| 79 * @return {boolean} | |
| 80 */ | |
| 81 isIdentity: function() | |
| 82 { | |
| 83 return true; | |
| 84 }, | |
| 85 | |
| 86 /** | |
| 87 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 88 * @param {number} lineNumber | |
| 89 * @return {boolean} | |
| 90 */ | |
| 91 uiLineHasMapping: function(uiSourceCode, lineNumber) | |
| 92 { | |
| 93 return true; | |
| 94 }, | |
| 95 | |
| 96 /** | |
| 97 * @return {!WebInspector.Target} | |
| 98 */ | |
| 99 target: function() | |
| 100 { | |
| 101 return this._cssModel.target(); | |
| 102 }, | |
| 103 | |
| 104 /** | |
| 105 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 106 */ | |
| 107 addHeader: function(header) | |
| 108 { | |
| 109 var url = header.resourceURL(); | |
| 110 if (!url) | |
| 111 return; | |
| 112 | |
| 113 WebInspector.cssWorkspaceBinding.pushSourceMapping(header, this); | |
| 114 var map = this._urlToHeadersByFrameId[url]; | |
| 115 if (!map) { | |
| 116 map = /** @type {!StringMap.<!StringMap.<!WebInspector.CSSStyleSheet
Header>>} */ (new StringMap()); | |
| 117 this._urlToHeadersByFrameId[url] = map; | |
| 118 } | |
| 119 var headersById = map.get(header.frameId); | |
| 120 if (!headersById) { | |
| 121 headersById = /** @type {!StringMap.<!WebInspector.CSSStyleSheetHead
er>} */ (new StringMap()); | |
| 122 map.put(header.frameId, headersById); | |
| 123 } | |
| 124 headersById.put(header.id, header); | |
| 125 var uiSourceCode = this._workspace.uiSourceCodeForURL(url); | |
| 126 if (uiSourceCode) | |
| 127 this._bindUISourceCode(uiSourceCode, header); | |
| 128 }, | |
| 129 | |
| 130 /** | |
| 131 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 132 */ | |
| 133 removeHeader: function(header) | |
| 134 { | |
| 135 var url = header.resourceURL(); | |
| 136 if (!url) | |
| 137 return; | |
| 138 | |
| 139 var map = this._urlToHeadersByFrameId[url]; | |
| 140 console.assert(map); | |
| 141 var headersById = map.get(header.frameId); | |
| 142 console.assert(headersById); | |
| 143 headersById.remove(header.id); | |
| 144 | |
| 145 if (!headersById.size()) { | |
| 146 map.remove(header.frameId); | |
| 147 if (!map.size()) { | |
| 148 delete this._urlToHeadersByFrameId[url]; | |
| 149 var uiSourceCode = this._workspace.uiSourceCodeForURL(url); | |
| 150 if (uiSourceCode) | |
| 151 this._unbindUISourceCode(uiSourceCode); | |
| 152 } | |
| 153 } | |
| 154 }, | |
| 155 | |
| 156 /** | |
| 157 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 158 */ | |
| 159 _unbindUISourceCode: function(uiSourceCode) | |
| 160 { | |
| 161 var styleFile = this._styleFiles.get(uiSourceCode); | |
| 162 if (!styleFile) | |
| 163 return; | |
| 164 styleFile.dispose(); | |
| 165 this._styleFiles.remove(uiSourceCode); | |
| 166 }, | |
| 167 | |
| 168 /** | |
| 169 * @param {!WebInspector.Event} event | |
| 170 */ | |
| 171 _uiSourceCodeAddedToWorkspace: function(event) | |
| 172 { | |
| 173 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
); | |
| 174 var url = uiSourceCode.url; | |
| 175 if (!url || !this._urlToHeadersByFrameId[url]) | |
| 176 return; | |
| 177 this._bindUISourceCode(uiSourceCode, this._urlToHeadersByFrameId[url].va
lues()[0].values()[0]); | |
| 178 }, | |
| 179 | |
| 180 /** | |
| 181 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 182 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 183 */ | |
| 184 _bindUISourceCode: function(uiSourceCode, header) | |
| 185 { | |
| 186 if (this._styleFiles.get(uiSourceCode) || header.isInline) | |
| 187 return; | |
| 188 var url = uiSourceCode.url; | |
| 189 this._styleFiles.put(uiSourceCode, new WebInspector.StyleFile(uiSourceCo
de, this)); | |
| 190 WebInspector.cssWorkspaceBinding.updateLocations(header); | |
| 191 }, | |
| 192 | |
| 193 /** | |
| 194 * @param {!WebInspector.Event} event | |
| 195 */ | |
| 196 _projectRemoved: function(event) | |
| 197 { | |
| 198 var project = /** @type {!WebInspector.Project} */ (event.data); | |
| 199 var uiSourceCodes = project.uiSourceCodes(); | |
| 200 for (var i = 0; i < uiSourceCodes.length; ++i) | |
| 201 this._unbindUISourceCode(uiSourceCodes[i]); | |
| 202 }, | |
| 203 | |
| 204 /** | |
| 205 * @param {!WebInspector.Event} event | |
| 206 */ | |
| 207 _uiSourceCodeRemoved: function(event) | |
| 208 { | |
| 209 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
); | |
| 210 this._unbindUISourceCode(uiSourceCode); | |
| 211 }, | |
| 212 | |
| 213 _initialize: function() | |
| 214 { | |
| 215 /** @type {!Object.<string, !StringMap.<!StringMap.<!WebInspector.CSSSty
leSheetHeader>>>} */ | |
| 216 this._urlToHeadersByFrameId = {}; | |
| 217 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.StyleFile>} *
/ | |
| 218 this._styleFiles = new Map(); | |
| 219 }, | |
| 220 | |
| 221 /** | |
| 222 * @param {!WebInspector.Event} event | |
| 223 */ | |
| 224 _mainFrameNavigated: function(event) | |
| 225 { | |
| 226 for (var url in this._urlToHeadersByFrameId) { | |
| 227 var uiSourceCode = this._workspace.uiSourceCodeForURL(url); | |
| 228 if (!uiSourceCode) | |
| 229 continue; | |
| 230 this._unbindUISourceCode(uiSourceCode); | |
| 231 } | |
| 232 this._initialize(); | |
| 233 }, | |
| 234 | |
| 235 /** | |
| 236 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 237 * @param {string} content | |
| 238 * @param {boolean} majorChange | |
| 239 * @param {function(?string)} userCallback | |
| 240 */ | |
| 241 _setStyleContent: function(uiSourceCode, content, majorChange, userCallback) | |
| 242 { | |
| 243 var styleSheetIds = this._cssModel.styleSheetIdsForURL(uiSourceCode.url)
; | |
| 244 if (!styleSheetIds.length) { | |
| 245 userCallback("No stylesheet found: " + uiSourceCode.url); | |
| 246 return; | |
| 247 } | |
| 248 | |
| 249 this._isSettingContent = true; | |
| 250 | |
| 251 /** | |
| 252 * @param {?Protocol.Error} error | |
| 253 * @this {WebInspector.StylesSourceMapping} | |
| 254 */ | |
| 255 function callback(error) | |
| 256 { | |
| 257 userCallback(error); | |
| 258 delete this._isSettingContent; | |
| 259 } | |
| 260 this._cssModel.setStyleSheetText(styleSheetIds[0], content, majorChange,
callback.bind(this)); | |
| 261 }, | |
| 262 | |
| 263 /** | |
| 264 * @param {!WebInspector.Event} event | |
| 265 */ | |
| 266 _styleSheetChanged: function(event) | |
| 267 { | |
| 268 if (this._isSettingContent) | |
| 269 return; | |
| 270 | |
| 271 if (event.data.majorChange) { | |
| 272 this._updateStyleSheetText(event.data.styleSheetId); | |
| 273 return; | |
| 274 } | |
| 275 | |
| 276 this._updateStyleSheetTextSoon(event.data.styleSheetId); | |
| 277 }, | |
| 278 | |
| 279 /** | |
| 280 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 281 */ | |
| 282 _updateStyleSheetTextSoon: function(styleSheetId) | |
| 283 { | |
| 284 if (this._updateStyleSheetTextTimer) | |
| 285 clearTimeout(this._updateStyleSheetTextTimer); | |
| 286 | |
| 287 this._updateStyleSheetTextTimer = setTimeout(this._updateStyleSheetText.
bind(this, styleSheetId), WebInspector.StylesSourceMapping.MinorChangeUpdateTime
outMs); | |
| 288 }, | |
| 289 | |
| 290 /** | |
| 291 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 292 */ | |
| 293 _updateStyleSheetText: function(styleSheetId) | |
| 294 { | |
| 295 if (this._updateStyleSheetTextTimer) { | |
| 296 clearTimeout(this._updateStyleSheetTextTimer); | |
| 297 delete this._updateStyleSheetTextTimer; | |
| 298 } | |
| 299 | |
| 300 var header = this._cssModel.styleSheetHeaderForId(styleSheetId); | |
| 301 if (!header) | |
| 302 return; | |
| 303 var styleSheetURL = header.resourceURL(); | |
| 304 if (!styleSheetURL) | |
| 305 return; | |
| 306 var uiSourceCode = this._workspace.uiSourceCodeForURL(styleSheetURL) | |
| 307 if (!uiSourceCode) | |
| 308 return; | |
| 309 header.requestContent(callback.bind(this, uiSourceCode)); | |
| 310 | |
| 311 /** | |
| 312 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 313 * @param {?string} content | |
| 314 * @this {WebInspector.StylesSourceMapping} | |
| 315 */ | |
| 316 function callback(uiSourceCode, content) | |
| 317 { | |
| 318 var styleFile = this._styleFiles.get(uiSourceCode); | |
| 319 if (styleFile) | |
| 320 styleFile.addRevision(content || ""); | |
| 321 } | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 /** | |
| 326 * @constructor | |
| 327 * @param {!WebInspector.UISourceCode} uiSourceCode | |
| 328 * @param {!WebInspector.StylesSourceMapping} mapping | |
| 329 */ | |
| 330 WebInspector.StyleFile = function(uiSourceCode, mapping) | |
| 331 { | |
| 332 this._uiSourceCode = uiSourceCode; | |
| 333 this._mapping = mapping; | |
| 334 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.Working
CopyChanged, this._workingCopyChanged, this); | |
| 335 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.Working
CopyCommitted, this._workingCopyCommitted, this); | |
| 336 this._commitThrottler = new WebInspector.Throttler(WebInspector.StyleFile.up
dateTimeout); | |
| 337 } | |
| 338 | |
| 339 WebInspector.StyleFile.updateTimeout = 200; | |
| 340 | |
| 341 WebInspector.StyleFile.prototype = { | |
| 342 /** | |
| 343 * @param {!WebInspector.Event} event | |
| 344 */ | |
| 345 _workingCopyCommitted: function(event) | |
| 346 { | |
| 347 if (this._isAddingRevision) | |
| 348 return; | |
| 349 | |
| 350 this._isMajorChangePending = true; | |
| 351 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), t
rue); | |
| 352 }, | |
| 353 | |
| 354 /** | |
| 355 * @param {!WebInspector.Event} event | |
| 356 */ | |
| 357 _workingCopyChanged: function(event) | |
| 358 { | |
| 359 if (this._isAddingRevision) | |
| 360 return; | |
| 361 | |
| 362 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), f
alse); | |
| 363 }, | |
| 364 | |
| 365 /** | |
| 366 * @param {!WebInspector.Throttler.FinishCallback} finishCallback | |
| 367 */ | |
| 368 _commitIncrementalEdit: function(finishCallback) | |
| 369 { | |
| 370 this._mapping._setStyleContent(this._uiSourceCode, this._uiSourceCode.wo
rkingCopy(), this._isMajorChangePending, this._styleContentSet.bind(this, finish
Callback)); | |
| 371 this._isMajorChangePending = false; | |
| 372 }, | |
| 373 | |
| 374 /** | |
| 375 * @param {!WebInspector.Throttler.FinishCallback} finishCallback | |
| 376 * @param {?string} error | |
| 377 */ | |
| 378 _styleContentSet: function(finishCallback, error) | |
| 379 { | |
| 380 if (error) | |
| 381 WebInspector.console.error(error); | |
| 382 finishCallback(); | |
| 383 }, | |
| 384 | |
| 385 /** | |
| 386 * @param {string} content | |
| 387 */ | |
| 388 addRevision: function(content) | |
| 389 { | |
| 390 this._isAddingRevision = true; | |
| 391 this._uiSourceCode.addRevision(content); | |
| 392 delete this._isAddingRevision; | |
| 393 }, | |
| 394 | |
| 395 dispose: function() | |
| 396 { | |
| 397 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.
WorkingCopyCommitted, this._workingCopyCommitted, this); | |
| 398 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.
WorkingCopyChanged, this._workingCopyChanged, this); | |
| 399 } | |
| 400 } | |
| OLD | NEW |