| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 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 |
| 11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. | 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
| 17 * | 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | |
| 31 /** | 30 /** |
| 32 * @constructor | 31 * @unrestricted |
| 33 * @extends {WebInspector.SDKModel} | |
| 34 * @param {!WebInspector.Target} target | |
| 35 * @param {!WebInspector.DOMModel} domModel | |
| 36 */ | 32 */ |
| 37 WebInspector.CSSModel = function(target, domModel) | 33 WebInspector.CSSModel = class extends WebInspector.SDKModel { |
| 38 { | 34 /** |
| 39 WebInspector.SDKModel.call(this, WebInspector.CSSModel, target); | 35 * @param {!WebInspector.Target} target |
| 36 * @param {!WebInspector.DOMModel} domModel |
| 37 */ |
| 38 constructor(target, domModel) { |
| 39 super(WebInspector.CSSModel, target); |
| 40 this._domModel = domModel; | 40 this._domModel = domModel; |
| 41 this._agent = target.cssAgent(); | 41 this._agent = target.cssAgent(); |
| 42 this._styleLoader = new WebInspector.CSSModel.ComputedStyleLoader(this); | 42 this._styleLoader = new WebInspector.CSSModel.ComputedStyleLoader(this); |
| 43 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Event
s.MainFrameNavigated, this._mainFrameNavigated, this); | 43 WebInspector.targetManager.addEventListener( |
| 44 WebInspector.TargetManager.Events.MainFrameNavigated, this._mainFrameNav
igated, this); |
| 44 target.registerCSSDispatcher(new WebInspector.CSSDispatcher(this)); | 45 target.registerCSSDispatcher(new WebInspector.CSSDispatcher(this)); |
| 45 this._agent.enable().then(this._wasEnabled.bind(this)); | 46 this._agent.enable().then(this._wasEnabled.bind(this)); |
| 46 /** @type {!Map.<string, !WebInspector.CSSStyleSheetHeader>} */ | 47 /** @type {!Map.<string, !WebInspector.CSSStyleSheetHeader>} */ |
| 47 this._styleSheetIdToHeader = new Map(); | 48 this._styleSheetIdToHeader = new Map(); |
| 48 /** @type {!Map.<string, !Object.<!PageAgent.FrameId, !Array.<!CSSAgent.Styl
eSheetId>>>} */ | 49 /** @type {!Map.<string, !Object.<!PageAgent.FrameId, !Array.<!CSSAgent.Styl
eSheetId>>>} */ |
| 49 this._styleSheetIdsForURL = new Map(); | 50 this._styleSheetIdsForURL = new Map(); |
| 50 | 51 |
| 51 /** @type {!Map.<!WebInspector.CSSStyleSheetHeader, !Promise<string>>} */ | 52 /** @type {!Map.<!WebInspector.CSSStyleSheetHeader, !Promise<string>>} */ |
| 52 this._originalStyleSheetText = new Map(); | 53 this._originalStyleSheetText = new Map(); |
| 53 | 54 |
| 54 /** @type {!Multimap<string, !CSSAgent.StyleSheetId>} */ | 55 /** @type {!Multimap<string, !CSSAgent.StyleSheetId>} */ |
| 55 this._sourceMapLoadingStyleSheetsIds = new Multimap(); | 56 this._sourceMapLoadingStyleSheetsIds = new Multimap(); |
| 56 | 57 |
| 57 /** @type {!Map<string, !WebInspector.SourceMap>} */ | 58 /** @type {!Map<string, !WebInspector.SourceMap>} */ |
| 58 this._sourceMapByURL = new Map(); | 59 this._sourceMapByURL = new Map(); |
| 59 /** @type {!Multimap<string, !WebInspector.CSSStyleSheetHeader>} */ | 60 /** @type {!Multimap<string, !WebInspector.CSSStyleSheetHeader>} */ |
| 60 this._sourceMapURLToHeaders = new Multimap(); | 61 this._sourceMapURLToHeaders = new Multimap(); |
| 61 WebInspector.moduleSetting("cssSourceMapsEnabled").addChangeListener(this._t
oggleSourceMapSupport, this); | 62 WebInspector.moduleSetting('cssSourceMapsEnabled').addChangeListener(this._t
oggleSourceMapSupport, this); |
| 63 } |
| 64 |
| 65 /** |
| 66 * @param {string} text |
| 67 * @return {string} |
| 68 */ |
| 69 static trimSourceURL(text) { |
| 70 var sourceURLIndex = text.lastIndexOf('/*# sourceURL='); |
| 71 if (sourceURLIndex === -1) { |
| 72 sourceURLIndex = text.lastIndexOf('/*@ sourceURL='); |
| 73 if (sourceURLIndex === -1) |
| 74 return text; |
| 75 } |
| 76 var sourceURLLineIndex = text.lastIndexOf('\n', sourceURLIndex); |
| 77 if (sourceURLLineIndex === -1) |
| 78 return text; |
| 79 var sourceURLLine = text.substr(sourceURLLineIndex + 1).split('\n', 1)[0]; |
| 80 var sourceURLRegex = /[\040\t]*\/\*[#@] sourceURL=[\040\t]*([^\s]*)[\040\t]*
\*\/[\040\t]*$/; |
| 81 if (sourceURLLine.search(sourceURLRegex) === -1) |
| 82 return text; |
| 83 return text.substr(0, sourceURLLineIndex) + text.substr(sourceURLLineIndex +
sourceURLLine.length + 1); |
| 84 } |
| 85 |
| 86 /** |
| 87 * @param {!WebInspector.Target} target |
| 88 * @return {?WebInspector.CSSModel} |
| 89 */ |
| 90 static fromTarget(target) { |
| 91 return /** @type {?WebInspector.CSSModel} */ (target.model(WebInspector.CSSM
odel)); |
| 92 } |
| 93 |
| 94 /** |
| 95 * @param {!WebInspector.DOMNode} node |
| 96 * @return {!WebInspector.CSSModel} |
| 97 */ |
| 98 static fromNode(node) { |
| 99 return /** @type {!WebInspector.CSSModel} */ (WebInspector.CSSModel.fromTarg
et(node.target())); |
| 100 } |
| 101 |
| 102 /** |
| 103 * @param {!WebInspector.Event} event |
| 104 */ |
| 105 _toggleSourceMapSupport(event) { |
| 106 var enabled = /** @type {boolean} */ (event.data); |
| 107 var headers = this.styleSheetHeaders(); |
| 108 for (var header of headers) { |
| 109 if (enabled) |
| 110 this._attachSourceMap(header); |
| 111 else |
| 112 this._detachSourceMap(header); |
| 113 } |
| 114 } |
| 115 |
| 116 /** |
| 117 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 118 * @return {?WebInspector.SourceMap} |
| 119 */ |
| 120 sourceMapForHeader(header) { |
| 121 return this._sourceMapByURL.get(header.sourceMapURL) || null; |
| 122 } |
| 123 |
| 124 _sourceMapLoadedForTest() { |
| 125 } |
| 126 |
| 127 /** |
| 128 * @param {!WebInspector.SourceMap} sourceMap |
| 129 * @return {!Array<!WebInspector.CSSStyleSheetHeader>} |
| 130 */ |
| 131 headersForSourceMap(sourceMap) { |
| 132 return this._sourceMapURLToHeaders.get(sourceMap.url()).valuesArray(); |
| 133 } |
| 134 |
| 135 /** |
| 136 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 137 */ |
| 138 _attachSourceMap(header) { |
| 139 var sourceMapURL = header.sourceMapURL; |
| 140 if (!sourceMapURL || !WebInspector.moduleSetting('cssSourceMapsEnabled').get
()) |
| 141 return; |
| 142 if (this._sourceMapByURL.has(sourceMapURL)) { |
| 143 attach.call(this, sourceMapURL, header); |
| 144 return; |
| 145 } |
| 146 if (!this._sourceMapLoadingStyleSheetsIds.has(sourceMapURL)) { |
| 147 WebInspector.TextSourceMap.load(sourceMapURL, header.sourceURL) |
| 148 .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) |
| 149 .then(onSourceMap.bind(this, sourceMapURL)); |
| 150 } |
| 151 this._sourceMapLoadingStyleSheetsIds.set(sourceMapURL, header.id); |
| 152 |
| 153 /** |
| 154 * @param {string} sourceMapURL |
| 155 * @param {?WebInspector.TextSourceMap} sourceMap |
| 156 * @return {!Promise<?WebInspector.SourceMap>} |
| 157 * @this {WebInspector.CSSModel} |
| 158 */ |
| 159 function onTextSourceMapLoaded(sourceMapURL, sourceMap) { |
| 160 if (!sourceMap) |
| 161 return Promise.resolve(/** @type {?WebInspector.SourceMap} */ (null)); |
| 162 var factoryExtension = this._factoryForSourceMap(sourceMap); |
| 163 if (!factoryExtension) |
| 164 return Promise.resolve(/** @type {?WebInspector.SourceMap} */ (sourceMap
)); |
| 165 return factoryExtension.instance() |
| 166 .then(factory => factory.editableSourceMap(this.target(), sourceMap)) |
| 167 .then(map => map || sourceMap) |
| 168 .catchException(/** @type {?WebInspector.SourceMap} */ (null)); |
| 169 } |
| 170 |
| 171 /** |
| 172 * @param {string} sourceMapURL |
| 173 * @param {?WebInspector.SourceMap} sourceMap |
| 174 * @this {WebInspector.CSSModel} |
| 175 */ |
| 176 function onSourceMap(sourceMapURL, sourceMap) { |
| 177 this._sourceMapLoadedForTest(); |
| 178 var styleSheetIds = this._sourceMapLoadingStyleSheetsIds.get(sourceMapURL)
; |
| 179 this._sourceMapLoadingStyleSheetsIds.removeAll(sourceMapURL); |
| 180 if (!sourceMap) |
| 181 return; |
| 182 var headers = new Set(); |
| 183 for (var styleSheetId of styleSheetIds) { |
| 184 var header = this.styleSheetHeaderForId(styleSheetId); |
| 185 if (header) |
| 186 headers.add(header); |
| 187 } |
| 188 if (!headers.size) |
| 189 return; |
| 190 this._sourceMapByURL.set(sourceMapURL, sourceMap); |
| 191 for (var header of headers) |
| 192 attach.call(this, sourceMapURL, header); |
| 193 } |
| 194 |
| 195 /** |
| 196 * @param {string} sourceMapURL |
| 197 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 198 * @this {WebInspector.CSSModel} |
| 199 */ |
| 200 function attach(sourceMapURL, header) { |
| 201 this._sourceMapURLToHeaders.set(sourceMapURL, header); |
| 202 this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapAttach
ed, header); |
| 203 } |
| 204 } |
| 205 |
| 206 /** |
| 207 * @param {!WebInspector.SourceMap} sourceMap |
| 208 * @return {?Runtime.Extension} |
| 209 */ |
| 210 _factoryForSourceMap(sourceMap) { |
| 211 var sourceExtensions = new Set(); |
| 212 for (var url of sourceMap.sourceURLs()) |
| 213 sourceExtensions.add(WebInspector.ParsedURL.extractExtension(url)); |
| 214 for (var runtimeExtension of self.runtime.extensions(WebInspector.SourceMapF
actory)) { |
| 215 var supportedExtensions = new Set(runtimeExtension.descriptor()['extension
s']); |
| 216 if (supportedExtensions.containsAll(sourceExtensions)) |
| 217 return runtimeExtension; |
| 218 } |
| 219 return null; |
| 220 } |
| 221 |
| 222 /** |
| 223 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 224 */ |
| 225 _detachSourceMap(header) { |
| 226 if (!header.sourceMapURL || !this._sourceMapURLToHeaders.hasValue(header.sou
rceMapURL, header)) |
| 227 return; |
| 228 this._sourceMapURLToHeaders.remove(header.sourceMapURL, header); |
| 229 if (!this._sourceMapURLToHeaders.has(header.sourceMapURL)) |
| 230 this._sourceMapByURL.delete(header.sourceMapURL); |
| 231 this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapDetached
, header); |
| 232 } |
| 233 |
| 234 /** |
| 235 * @return {!WebInspector.DOMModel} |
| 236 */ |
| 237 domModel() { |
| 238 return this._domModel; |
| 239 } |
| 240 |
| 241 /** |
| 242 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 243 * @param {!WebInspector.TextRange} range |
| 244 * @param {string} text |
| 245 * @param {boolean} majorChange |
| 246 * @return {!Promise<boolean>} |
| 247 */ |
| 248 setStyleText(styleSheetId, range, text, majorChange) { |
| 249 var original = this._innerSetStyleTexts.bind(this, [styleSheetId], [range],
[text], majorChange); |
| 250 var header = this.styleSheetHeaderForId(styleSheetId); |
| 251 if (!header) |
| 252 return original(); |
| 253 |
| 254 var sourceMap = this.sourceMapForHeader(header); |
| 255 if (!sourceMap) |
| 256 return original(); |
| 257 |
| 258 var originalAndDetach = originalAndDetachIfSuccess.bind(this, header); |
| 259 |
| 260 if (!sourceMap.editable()) |
| 261 return original(); |
| 262 |
| 263 return /** @type {!Promise<boolean>} */ ( |
| 264 sourceMap.editCompiled([range], [text]).then(onEditingDone.bind(this)).c
atch(onError.bind(this, header))); |
| 265 |
| 266 /** |
| 267 * @param {?WebInspector.SourceMap.EditResult} editResult |
| 268 * @return {!Promise<boolean>} |
| 269 * @this {WebInspector.CSSModel} |
| 270 */ |
| 271 function onEditingDone(editResult) { |
| 272 if (!editResult) |
| 273 return Promise.resolve(false); |
| 274 |
| 275 var edits = editResult.compiledEdits; |
| 276 if (!edits.length) |
| 277 return onCSSPatched.call(this, editResult, true); |
| 278 |
| 279 edits.sort(WebInspector.SourceEdit.comparator); |
| 280 edits = edits.reverse(); |
| 281 |
| 282 var styleSheetIds = []; |
| 283 var ranges = []; |
| 284 var texts = []; |
| 285 for (var edit of edits) { |
| 286 styleSheetIds.push(header.id); |
| 287 ranges.push(edit.oldRange); |
| 288 texts.push(edit.newText); |
| 289 } |
| 290 return this._innerSetStyleTexts(styleSheetIds, ranges, texts, majorChange) |
| 291 .then(onCSSPatched.bind(this, editResult)); |
| 292 } |
| 293 |
| 294 /** |
| 295 * @param {!WebInspector.SourceMap.EditResult} editResult |
| 296 * @param {boolean} success |
| 297 * @return {!Promise<boolean>} |
| 298 * @this {WebInspector.CSSModel} |
| 299 */ |
| 300 function onCSSPatched(editResult, success) { |
| 301 if (!success) |
| 302 return originalAndDetach(); |
| 303 |
| 304 this._sourceMapByURL.set(header.sourceMapURL, editResult.map); |
| 305 this.dispatchEventToListeners( |
| 306 WebInspector.CSSModel.Events.SourceMapChanged, |
| 307 {sourceMap: editResult.map, newSources: editResult.newSources}); |
| 308 return Promise.resolve(true); |
| 309 } |
| 310 |
| 311 /** |
| 312 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 313 * @param {*} error |
| 314 * @return {!Promise<boolean>} |
| 315 * @this {WebInspector.CSSModel} |
| 316 */ |
| 317 function onError(header, error) { |
| 318 WebInspector.console.error(WebInspector.UIString('LiveSASS failed: %s', so
urceMap.compiledURL())); |
| 319 console.error(error); |
| 320 this._detachSourceMap(header); |
| 321 return original(); |
| 322 } |
| 323 |
| 324 /** |
| 325 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 326 * @return {!Promise<boolean>} |
| 327 * @this {WebInspector.CSSModel} |
| 328 */ |
| 329 function originalAndDetachIfSuccess(header) { |
| 330 return this._innerSetStyleTexts([styleSheetId], [range], [text], majorChan
ge).then(detachIfSuccess.bind(this)); |
| 331 |
| 332 /** |
| 333 * @param {boolean} success |
| 334 * @return {boolean} |
| 335 * @this {WebInspector.CSSModel} |
| 336 */ |
| 337 function detachIfSuccess(success) { |
| 338 if (success) |
| 339 this._detachSourceMap(header); |
| 340 return success; |
| 341 } |
| 342 } |
| 343 } |
| 344 |
| 345 /** |
| 346 * @param {!Array<!CSSAgent.StyleSheetId>} styleSheetIds |
| 347 * @param {!Array<!WebInspector.TextRange>} ranges |
| 348 * @param {!Array<string>} texts |
| 349 * @param {boolean} majorChange |
| 350 * @return {!Promise<boolean>} |
| 351 */ |
| 352 _innerSetStyleTexts(styleSheetIds, ranges, texts, majorChange) { |
| 353 /** |
| 354 * @param {?Protocol.Error} error |
| 355 * @param {?Array<!CSSAgent.CSSStyle>} stylePayloads |
| 356 * @return {boolean} |
| 357 * @this {WebInspector.CSSModel} |
| 358 */ |
| 359 function parsePayload(error, stylePayloads) { |
| 360 if (error || !stylePayloads || stylePayloads.length !== ranges.length) |
| 361 return false; |
| 362 |
| 363 if (majorChange) |
| 364 this._domModel.markUndoableState(); |
| 365 for (var i = 0; i < ranges.length; ++i) { |
| 366 var edit = new WebInspector.CSSModel.Edit(styleSheetIds[i], ranges[i], t
exts[i], stylePayloads[i]); |
| 367 this._fireStyleSheetChanged(styleSheetIds[i], edit); |
| 368 } |
| 369 return true; |
| 370 } |
| 371 |
| 372 console.assert( |
| 373 styleSheetIds.length === ranges.length && ranges.length === texts.length
, 'Array lengths must be equal'); |
| 374 var edits = []; |
| 375 var ensureContentPromises = []; |
| 376 for (var i = 0; i < styleSheetIds.length; ++i) { |
| 377 edits.push({styleSheetId: styleSheetIds[i], range: ranges[i].serializeToOb
ject(), text: texts[i]}); |
| 378 ensureContentPromises.push(this._ensureOriginalStyleSheetText(styleSheetId
s[i])); |
| 379 } |
| 380 |
| 381 return Promise.all(ensureContentPromises) |
| 382 .then(() => this._agent.setStyleTexts(edits, parsePayload.bind(this))) |
| 383 .catchException(false); |
| 384 } |
| 385 |
| 386 /** |
| 387 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 388 * @param {!WebInspector.TextRange} range |
| 389 * @param {string} text |
| 390 * @return {!Promise<boolean>} |
| 391 */ |
| 392 setSelectorText(styleSheetId, range, text) { |
| 393 /** |
| 394 * @param {?Protocol.Error} error |
| 395 * @param {?CSSAgent.SelectorList} selectorPayload |
| 396 * @return {boolean} |
| 397 * @this {WebInspector.CSSModel} |
| 398 */ |
| 399 function callback(error, selectorPayload) { |
| 400 if (error || !selectorPayload) |
| 401 return false; |
| 402 this._domModel.markUndoableState(); |
| 403 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text, selec
torPayload); |
| 404 this._fireStyleSheetChanged(styleSheetId, edit); |
| 405 return true; |
| 406 } |
| 407 |
| 408 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.StyleRu
leEdited); |
| 409 return this._ensureOriginalStyleSheetText(styleSheetId) |
| 410 .then(() => this._agent.setRuleSelector(styleSheetId, range, text, callb
ack.bind(this))) |
| 411 .catchException(false); |
| 412 } |
| 413 |
| 414 /** |
| 415 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 416 * @param {!WebInspector.TextRange} range |
| 417 * @param {string} text |
| 418 * @return {!Promise<boolean>} |
| 419 */ |
| 420 setKeyframeKey(styleSheetId, range, text) { |
| 421 /** |
| 422 * @param {?Protocol.Error} error |
| 423 * @param {!CSSAgent.Value} payload |
| 424 * @return {boolean} |
| 425 * @this {WebInspector.CSSModel} |
| 426 */ |
| 427 function callback(error, payload) { |
| 428 if (error || !payload) |
| 429 return false; |
| 430 this._domModel.markUndoableState(); |
| 431 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text, paylo
ad); |
| 432 this._fireStyleSheetChanged(styleSheetId, edit); |
| 433 return true; |
| 434 } |
| 435 |
| 436 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.StyleRu
leEdited); |
| 437 return this._ensureOriginalStyleSheetText(styleSheetId) |
| 438 .then(() => this._agent.setKeyframeKey(styleSheetId, range, text, callba
ck.bind(this))) |
| 439 .catchException(false); |
| 440 } |
| 441 |
| 442 /** |
| 443 * @return {!Promise.<!Array.<!WebInspector.CSSMedia>>} |
| 444 */ |
| 445 mediaQueriesPromise() { |
| 446 /** |
| 447 * @param {?Protocol.Error} error |
| 448 * @param {?Array.<!CSSAgent.CSSMedia>} payload |
| 449 * @return {!Array.<!WebInspector.CSSMedia>} |
| 450 * @this {!WebInspector.CSSModel} |
| 451 */ |
| 452 function parsePayload(error, payload) { |
| 453 return !error && payload ? WebInspector.CSSMedia.parseMediaArrayPayload(th
is, payload) : []; |
| 454 } |
| 455 |
| 456 return this._agent.getMediaQueries(parsePayload.bind(this)); |
| 457 } |
| 458 |
| 459 /** |
| 460 * @return {boolean} |
| 461 */ |
| 462 isEnabled() { |
| 463 return this._isEnabled; |
| 464 } |
| 465 |
| 466 /** |
| 467 * @param {?Protocol.Error} error |
| 468 */ |
| 469 _wasEnabled(error) { |
| 470 if (error) { |
| 471 console.error('Failed to enabled CSS agent: ' + error); |
| 472 return; |
| 473 } |
| 474 this._isEnabled = true; |
| 475 this.dispatchEventToListeners(WebInspector.CSSModel.Events.ModelWasEnabled); |
| 476 } |
| 477 |
| 478 /** |
| 479 * @param {!DOMAgent.NodeId} nodeId |
| 480 * @return {!Promise.<?WebInspector.CSSMatchedStyles>} |
| 481 */ |
| 482 matchedStylesPromise(nodeId) { |
| 483 /** |
| 484 * @param {?Protocol.Error} error |
| 485 * @param {?CSSAgent.CSSStyle=} inlinePayload |
| 486 * @param {?CSSAgent.CSSStyle=} attributesPayload |
| 487 * @param {!Array.<!CSSAgent.RuleMatch>=} matchedPayload |
| 488 * @param {!Array.<!CSSAgent.PseudoElementMatches>=} pseudoPayload |
| 489 * @param {!Array.<!CSSAgent.InheritedStyleEntry>=} inheritedPayload |
| 490 * @param {!Array.<!CSSAgent.CSSKeyframesRule>=} animationsPayload |
| 491 * @return {?WebInspector.CSSMatchedStyles} |
| 492 * @this {WebInspector.CSSModel} |
| 493 */ |
| 494 function callback( |
| 495 error, inlinePayload, attributesPayload, matchedPayload, pseudoPayload,
inheritedPayload, animationsPayload) { |
| 496 if (error) |
| 497 return null; |
| 498 |
| 499 var node = this._domModel.nodeForId(nodeId); |
| 500 if (!node) |
| 501 return null; |
| 502 |
| 503 return new WebInspector.CSSMatchedStyles( |
| 504 this, node, inlinePayload || null, attributesPayload || null, matchedP
ayload || [], pseudoPayload || [], |
| 505 inheritedPayload || [], animationsPayload || []); |
| 506 } |
| 507 |
| 508 return this._agent.getMatchedStylesForNode(nodeId, callback.bind(this)); |
| 509 } |
| 510 |
| 511 /** |
| 512 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 513 * @return {!Promise<!Array<string>>} |
| 514 */ |
| 515 classNamesPromise(styleSheetId) { |
| 516 /** |
| 517 * @param {?string} error |
| 518 * @param {?Array<string>} classNames |
| 519 * @return {!Array<string>} |
| 520 */ |
| 521 function classNamesCallback(error, classNames) { |
| 522 return !error && classNames ? classNames : []; |
| 523 } |
| 524 return this._agent.collectClassNames(styleSheetId, classNamesCallback); |
| 525 } |
| 526 |
| 527 /** |
| 528 * @param {!DOMAgent.NodeId} nodeId |
| 529 * @return {!Promise.<?Map.<string, string>>} |
| 530 */ |
| 531 computedStylePromise(nodeId) { |
| 532 return this._styleLoader.computedStylePromise(nodeId); |
| 533 } |
| 534 |
| 535 /** |
| 536 * @param {number} nodeId |
| 537 * @return {!Promise<?Array<string>>} |
| 538 */ |
| 539 backgroundColorsPromise(nodeId) { |
| 540 /** |
| 541 * @param {?string} error |
| 542 * @param {!Array<string>=} backgroundColors |
| 543 * @return {?Array<string>} |
| 544 */ |
| 545 function backgroundColorsCallback(error, backgroundColors) { |
| 546 return !error && backgroundColors ? backgroundColors : null; |
| 547 } |
| 548 return this._agent.getBackgroundColors(nodeId, backgroundColorsCallback); |
| 549 } |
| 550 |
| 551 /** |
| 552 * @param {number} nodeId |
| 553 * @return {!Promise.<?Array.<!CSSAgent.PlatformFontUsage>>} |
| 554 */ |
| 555 platformFontsPromise(nodeId) { |
| 556 /** |
| 557 * @param {?Protocol.Error} error |
| 558 * @param {?Array.<!CSSAgent.PlatformFontUsage>} fonts |
| 559 * @return {?Array.<!CSSAgent.PlatformFontUsage>} |
| 560 */ |
| 561 function platformFontsCallback(error, fonts) { |
| 562 return !error && fonts ? fonts : null; |
| 563 } |
| 564 |
| 565 return this._agent.getPlatformFontsForNode(nodeId, platformFontsCallback); |
| 566 } |
| 567 |
| 568 /** |
| 569 * @return {!Array.<!WebInspector.CSSStyleSheetHeader>} |
| 570 */ |
| 571 allStyleSheets() { |
| 572 var values = this._styleSheetIdToHeader.valuesArray(); |
| 573 /** |
| 574 * @param {!WebInspector.CSSStyleSheetHeader} a |
| 575 * @param {!WebInspector.CSSStyleSheetHeader} b |
| 576 * @return {number} |
| 577 */ |
| 578 function styleSheetComparator(a, b) { |
| 579 if (a.sourceURL < b.sourceURL) |
| 580 return -1; |
| 581 else if (a.sourceURL > b.sourceURL) |
| 582 return 1; |
| 583 return a.startLine - b.startLine || a.startColumn - b.startColumn; |
| 584 } |
| 585 values.sort(styleSheetComparator); |
| 586 |
| 587 return values; |
| 588 } |
| 589 |
| 590 /** |
| 591 * @param {!DOMAgent.NodeId} nodeId |
| 592 * @return {!Promise.<?WebInspector.CSSModel.InlineStyleResult>} |
| 593 */ |
| 594 inlineStylesPromise(nodeId) { |
| 595 /** |
| 596 * @param {?Protocol.Error} error |
| 597 * @param {?CSSAgent.CSSStyle=} inlinePayload |
| 598 * @param {?CSSAgent.CSSStyle=} attributesStylePayload |
| 599 * @return {?WebInspector.CSSModel.InlineStyleResult} |
| 600 * @this {WebInspector.CSSModel} |
| 601 */ |
| 602 function callback(error, inlinePayload, attributesStylePayload) { |
| 603 if (error || !inlinePayload) |
| 604 return null; |
| 605 var inlineStyle = inlinePayload ? |
| 606 new WebInspector.CSSStyleDeclaration( |
| 607 this, null, inlinePayload, WebInspector.CSSStyleDeclaration.Type.I
nline) : |
| 608 null; |
| 609 var attributesStyle = attributesStylePayload ? |
| 610 new WebInspector.CSSStyleDeclaration( |
| 611 this, null, attributesStylePayload, WebInspector.CSSStyleDeclarati
on.Type.Attributes) : |
| 612 null; |
| 613 return new WebInspector.CSSModel.InlineStyleResult(inlineStyle, attributes
Style); |
| 614 } |
| 615 |
| 616 return this._agent.getInlineStylesForNode(nodeId, callback.bind(this)); |
| 617 } |
| 618 |
| 619 /** |
| 620 * @param {!WebInspector.DOMNode} node |
| 621 * @param {string} pseudoClass |
| 622 * @param {boolean} enable |
| 623 * @return {boolean} |
| 624 */ |
| 625 forcePseudoState(node, pseudoClass, enable) { |
| 626 var pseudoClasses = node.marker(WebInspector.CSSModel.PseudoStateMarker) ||
[]; |
| 627 if (enable) { |
| 628 if (pseudoClasses.indexOf(pseudoClass) >= 0) |
| 629 return false; |
| 630 pseudoClasses.push(pseudoClass); |
| 631 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, pseudoClasses); |
| 632 } else { |
| 633 if (pseudoClasses.indexOf(pseudoClass) < 0) |
| 634 return false; |
| 635 pseudoClasses.remove(pseudoClass); |
| 636 if (pseudoClasses.length) |
| 637 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, pseudoClasses); |
| 638 else |
| 639 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, null); |
| 640 } |
| 641 |
| 642 this._agent.forcePseudoState(node.id, pseudoClasses); |
| 643 this.dispatchEventToListeners( |
| 644 WebInspector.CSSModel.Events.PseudoStateForced, {node: node, pseudoClass
: pseudoClass, enable: enable}); |
| 645 return true; |
| 646 } |
| 647 |
| 648 /** |
| 649 * @param {!WebInspector.DOMNode} node |
| 650 * @return {?Array<string>} state |
| 651 */ |
| 652 pseudoState(node) { |
| 653 return node.marker(WebInspector.CSSModel.PseudoStateMarker) || []; |
| 654 } |
| 655 |
| 656 /** |
| 657 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 658 * @param {!WebInspector.TextRange} range |
| 659 * @param {string} newMediaText |
| 660 * @return {!Promise<boolean>} |
| 661 */ |
| 662 setMediaText(styleSheetId, range, newMediaText) { |
| 663 /** |
| 664 * @param {?Protocol.Error} error |
| 665 * @param {!CSSAgent.CSSMedia} mediaPayload |
| 666 * @return {boolean} |
| 667 * @this {WebInspector.CSSModel} |
| 668 */ |
| 669 function parsePayload(error, mediaPayload) { |
| 670 if (!mediaPayload) |
| 671 return false; |
| 672 this._domModel.markUndoableState(); |
| 673 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, newMediaTex
t, mediaPayload); |
| 674 this._fireStyleSheetChanged(styleSheetId, edit); |
| 675 return true; |
| 676 } |
| 677 |
| 678 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.StyleRu
leEdited); |
| 679 return this._ensureOriginalStyleSheetText(styleSheetId) |
| 680 .then(() => this._agent.setMediaText(styleSheetId, range, newMediaText,
parsePayload.bind(this))) |
| 681 .catchException(false); |
| 682 } |
| 683 |
| 684 /** |
| 685 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 686 * @param {string} ruleText |
| 687 * @param {!WebInspector.TextRange} ruleLocation |
| 688 * @return {!Promise<?WebInspector.CSSStyleRule>} |
| 689 */ |
| 690 addRule(styleSheetId, ruleText, ruleLocation) { |
| 691 return this._ensureOriginalStyleSheetText(styleSheetId) |
| 692 .then(() => this._agent.addRule(styleSheetId, ruleText, ruleLocation, pa
rsePayload.bind(this))) |
| 693 .catchException(/** @type {?WebInspector.CSSStyleRule} */ (null)); |
| 694 |
| 695 /** |
| 696 * @param {?Protocol.Error} error |
| 697 * @param {?CSSAgent.CSSRule} rulePayload |
| 698 * @return {?WebInspector.CSSStyleRule} |
| 699 * @this {WebInspector.CSSModel} |
| 700 */ |
| 701 function parsePayload(error, rulePayload) { |
| 702 if (error || !rulePayload) |
| 703 return null; |
| 704 this._domModel.markUndoableState(); |
| 705 var edit = new WebInspector.CSSModel.Edit(styleSheetId, ruleLocation, rule
Text, rulePayload); |
| 706 this._fireStyleSheetChanged(styleSheetId, edit); |
| 707 return new WebInspector.CSSStyleRule(this, rulePayload); |
| 708 } |
| 709 } |
| 710 |
| 711 /** |
| 712 * @param {!WebInspector.DOMNode} node |
| 713 * @param {function(?WebInspector.CSSStyleSheetHeader)} userCallback |
| 714 */ |
| 715 requestViaInspectorStylesheet(node, userCallback) { |
| 716 var frameId = node.frameId() || WebInspector.ResourceTreeModel.fromTarget(th
is.target()).mainFrame.id; |
| 717 var headers = this._styleSheetIdToHeader.valuesArray(); |
| 718 for (var i = 0; i < headers.length; ++i) { |
| 719 var styleSheetHeader = headers[i]; |
| 720 if (styleSheetHeader.frameId === frameId && styleSheetHeader.isViaInspecto
r()) { |
| 721 userCallback(styleSheetHeader); |
| 722 return; |
| 723 } |
| 724 } |
| 725 |
| 726 /** |
| 727 * @param {?Protocol.Error} error |
| 728 * @param {?CSSAgent.StyleSheetId} styleSheetId |
| 729 * @return {?WebInspector.CSSStyleSheetHeader} |
| 730 * @this {WebInspector.CSSModel} |
| 731 */ |
| 732 function innerCallback(error, styleSheetId) { |
| 733 return !error && styleSheetId ? this._styleSheetIdToHeader.get(styleSheetI
d) || null : null; |
| 734 } |
| 735 |
| 736 this._agent.createStyleSheet(frameId, innerCallback.bind(this)).catchExcepti
on(null).then(userCallback); |
| 737 } |
| 738 |
| 739 mediaQueryResultChanged() { |
| 740 this.dispatchEventToListeners(WebInspector.CSSModel.Events.MediaQueryResultC
hanged); |
| 741 } |
| 742 |
| 743 fontsUpdated() { |
| 744 this.dispatchEventToListeners(WebInspector.CSSModel.Events.FontsUpdated); |
| 745 } |
| 746 |
| 747 /** |
| 748 * @param {!CSSAgent.StyleSheetId} id |
| 749 * @return {?WebInspector.CSSStyleSheetHeader} |
| 750 */ |
| 751 styleSheetHeaderForId(id) { |
| 752 return this._styleSheetIdToHeader.get(id) || null; |
| 753 } |
| 754 |
| 755 /** |
| 756 * @return {!Array.<!WebInspector.CSSStyleSheetHeader>} |
| 757 */ |
| 758 styleSheetHeaders() { |
| 759 return this._styleSheetIdToHeader.valuesArray(); |
| 760 } |
| 761 |
| 762 /** |
| 763 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 764 * @param {!WebInspector.CSSModel.Edit=} edit |
| 765 */ |
| 766 _fireStyleSheetChanged(styleSheetId, edit) { |
| 767 this.dispatchEventToListeners( |
| 768 WebInspector.CSSModel.Events.StyleSheetChanged, {styleSheetId: styleShee
tId, edit: edit}); |
| 769 } |
| 770 |
| 771 /** |
| 772 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 773 * @return {!Promise<string>} |
| 774 */ |
| 775 _ensureOriginalStyleSheetText(styleSheetId) { |
| 776 var header = this.styleSheetHeaderForId(styleSheetId); |
| 777 if (!header) |
| 778 return Promise.resolve(''); |
| 779 var promise = this._originalStyleSheetText.get(header); |
| 780 if (!promise) { |
| 781 promise = this.getStyleSheetText(header.id); |
| 782 this._originalStyleSheetText.set(header, promise); |
| 783 this._originalContentRequestedForTest(header); |
| 784 } |
| 785 return promise; |
| 786 } |
| 787 |
| 788 /** |
| 789 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 790 */ |
| 791 _originalContentRequestedForTest(header) { |
| 792 } |
| 793 |
| 794 /** |
| 795 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 796 * @return {!Promise<string>} |
| 797 */ |
| 798 originalStyleSheetText(header) { |
| 799 return this._ensureOriginalStyleSheetText(header.id); |
| 800 } |
| 801 |
| 802 /** |
| 803 * @param {!CSSAgent.CSSStyleSheetHeader} header |
| 804 */ |
| 805 _styleSheetAdded(header) { |
| 806 console.assert(!this._styleSheetIdToHeader.get(header.styleSheetId)); |
| 807 var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(this, header); |
| 808 this._styleSheetIdToHeader.set(header.styleSheetId, styleSheetHeader); |
| 809 var url = styleSheetHeader.resourceURL(); |
| 810 if (!this._styleSheetIdsForURL.get(url)) |
| 811 this._styleSheetIdsForURL.set(url, {}); |
| 812 var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(url); |
| 813 var styleSheetIds = frameIdToStyleSheetIds[styleSheetHeader.frameId]; |
| 814 if (!styleSheetIds) { |
| 815 styleSheetIds = []; |
| 816 frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds; |
| 817 } |
| 818 styleSheetIds.push(styleSheetHeader.id); |
| 819 this._attachSourceMap(styleSheetHeader); |
| 820 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetAdded,
styleSheetHeader); |
| 821 } |
| 822 |
| 823 /** |
| 824 * @param {!CSSAgent.StyleSheetId} id |
| 825 */ |
| 826 _styleSheetRemoved(id) { |
| 827 var header = this._styleSheetIdToHeader.get(id); |
| 828 console.assert(header); |
| 829 if (!header) |
| 830 return; |
| 831 this._styleSheetIdToHeader.remove(id); |
| 832 var url = header.resourceURL(); |
| 833 var frameIdToStyleSheetIds = /** @type {!Object.<!PageAgent.FrameId, !Array.
<!CSSAgent.StyleSheetId>>} */ ( |
| 834 this._styleSheetIdsForURL.get(url)); |
| 835 console.assert(frameIdToStyleSheetIds, 'No frameId to styleSheetId map is av
ailable for given style sheet URL.'); |
| 836 frameIdToStyleSheetIds[header.frameId].remove(id); |
| 837 if (!frameIdToStyleSheetIds[header.frameId].length) { |
| 838 delete frameIdToStyleSheetIds[header.frameId]; |
| 839 if (!Object.keys(frameIdToStyleSheetIds).length) |
| 840 this._styleSheetIdsForURL.remove(url); |
| 841 } |
| 842 this._originalStyleSheetText.remove(header); |
| 843 this._detachSourceMap(header); |
| 844 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetRemoved
, header); |
| 845 } |
| 846 |
| 847 /** |
| 848 * @param {string} url |
| 849 * @return {!Array.<!CSSAgent.StyleSheetId>} |
| 850 */ |
| 851 styleSheetIdsForURL(url) { |
| 852 var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(url); |
| 853 if (!frameIdToStyleSheetIds) |
| 854 return []; |
| 855 |
| 856 var result = []; |
| 857 for (var frameId in frameIdToStyleSheetIds) |
| 858 result = result.concat(frameIdToStyleSheetIds[frameId]); |
| 859 return result; |
| 860 } |
| 861 |
| 862 /** |
| 863 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 864 * @param {string} newText |
| 865 * @param {boolean} majorChange |
| 866 * @return {!Promise.<?Protocol.Error>} |
| 867 */ |
| 868 setStyleSheetText(styleSheetId, newText, majorChange) { |
| 869 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (this._styleSh
eetIdToHeader.get(styleSheetId)); |
| 870 console.assert(header); |
| 871 newText = WebInspector.CSSModel.trimSourceURL(newText); |
| 872 if (header.hasSourceURL) |
| 873 newText += '\n/*# sourceURL=' + header.sourceURL + ' */'; |
| 874 return this._ensureOriginalStyleSheetText(styleSheetId) |
| 875 .then(() => this._agent.setStyleSheetText(header.id, newText, callback.b
ind(this))); |
| 876 |
| 877 /** |
| 878 * @param {?Protocol.Error} error |
| 879 * @param {string=} sourceMapURL |
| 880 * @return {?Protocol.Error} |
| 881 * @this {WebInspector.CSSModel} |
| 882 */ |
| 883 function callback(error, sourceMapURL) { |
| 884 this._detachSourceMap(header); |
| 885 header.setSourceMapURL(sourceMapURL); |
| 886 this._attachSourceMap(header); |
| 887 if (error) |
| 888 return error; |
| 889 if (majorChange) |
| 890 this._domModel.markUndoableState(); |
| 891 this._fireStyleSheetChanged(styleSheetId); |
| 892 return null; |
| 893 } |
| 894 } |
| 895 |
| 896 /** |
| 897 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 898 * @return {!Promise<string>} |
| 899 */ |
| 900 getStyleSheetText(styleSheetId) { |
| 901 /** |
| 902 * @param {?Protocol.Error} error |
| 903 * @param {?string} text |
| 904 * @return {string} |
| 905 */ |
| 906 function textCallback(error, text) { |
| 907 if (error || text === null) { |
| 908 console.error('Failed to get text for stylesheet ' + styleSheetId + ': '
+ error); |
| 909 text = ''; |
| 910 // Fall through. |
| 911 } |
| 912 return WebInspector.CSSModel.trimSourceURL(text); |
| 913 } |
| 914 |
| 915 return this._agent.getStyleSheetText(styleSheetId, textCallback).catchExcept
ion(/** @type {string} */ ('')); |
| 916 } |
| 917 |
| 918 /** |
| 919 * @param {!WebInspector.Event} event |
| 920 */ |
| 921 _mainFrameNavigated(event) { |
| 922 if (event.data.target() !== this.target()) |
| 923 return; |
| 924 this._resetStyleSheets(); |
| 925 } |
| 926 |
| 927 _resetStyleSheets() { |
| 928 var headers = this._styleSheetIdToHeader.valuesArray(); |
| 929 this._styleSheetIdsForURL.clear(); |
| 930 this._styleSheetIdToHeader.clear(); |
| 931 for (var i = 0; i < headers.length; ++i) { |
| 932 this._detachSourceMap(headers[i]); |
| 933 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetRemov
ed, headers[i]); |
| 934 } |
| 935 this._sourceMapByURL.clear(); |
| 936 this._sourceMapURLToHeaders.clear(); |
| 937 this._sourceMapLoadingStyleSheetsIds.clear(); |
| 938 } |
| 939 |
| 940 /** |
| 941 * @override |
| 942 * @return {!Promise} |
| 943 */ |
| 944 suspendModel() { |
| 945 this._isEnabled = false; |
| 946 return this._agent.disable().then(this._resetStyleSheets.bind(this)); |
| 947 } |
| 948 |
| 949 /** |
| 950 * @override |
| 951 * @return {!Promise} |
| 952 */ |
| 953 resumeModel() { |
| 954 return this._agent.enable().then(this._wasEnabled.bind(this)); |
| 955 } |
| 956 |
| 957 /** |
| 958 * @param {!CSSAgent.StyleSheetId} id |
| 959 * @param {!CSSAgent.SourceRange} range |
| 960 */ |
| 961 _layoutEditorChange(id, range) { |
| 962 this.dispatchEventToListeners(WebInspector.CSSModel.Events.LayoutEditorChang
e, {id: id, range: range}); |
| 963 } |
| 964 |
| 965 /** |
| 966 * @param {number} nodeId |
| 967 * @param {string} name |
| 968 * @param {string} value |
| 969 */ |
| 970 setEffectivePropertyValueForNode(nodeId, name, value) { |
| 971 this._agent.setEffectivePropertyValueForNode(nodeId, name, value); |
| 972 } |
| 973 |
| 974 /** |
| 975 * @param {!WebInspector.DOMNode} node |
| 976 * @return {!Promise.<?WebInspector.CSSMatchedStyles>} |
| 977 */ |
| 978 cachedMatchedCascadeForNode(node) { |
| 979 if (this._cachedMatchedCascadeNode !== node) |
| 980 this.discardCachedMatchedCascade(); |
| 981 this._cachedMatchedCascadeNode = node; |
| 982 if (!this._cachedMatchedCascadePromise) |
| 983 this._cachedMatchedCascadePromise = this.matchedStylesPromise(node.id); |
| 984 return this._cachedMatchedCascadePromise; |
| 985 } |
| 986 |
| 987 discardCachedMatchedCascade() { |
| 988 delete this._cachedMatchedCascadeNode; |
| 989 delete this._cachedMatchedCascadePromise; |
| 990 } |
| 62 }; | 991 }; |
| 63 | 992 |
| 64 /** @enum {symbol} */ | 993 /** @enum {symbol} */ |
| 65 WebInspector.CSSModel.Events = { | 994 WebInspector.CSSModel.Events = { |
| 66 LayoutEditorChange: Symbol("LayoutEditorChange"), | 995 LayoutEditorChange: Symbol('LayoutEditorChange'), |
| 67 FontsUpdated: Symbol("FontsUpdated"), | 996 FontsUpdated: Symbol('FontsUpdated'), |
| 68 MediaQueryResultChanged: Symbol("MediaQueryResultChanged"), | 997 MediaQueryResultChanged: Symbol('MediaQueryResultChanged'), |
| 69 ModelWasEnabled: Symbol("ModelWasEnabled"), | 998 ModelWasEnabled: Symbol('ModelWasEnabled'), |
| 70 PseudoStateForced: Symbol("PseudoStateForced"), | 999 PseudoStateForced: Symbol('PseudoStateForced'), |
| 71 StyleSheetAdded: Symbol("StyleSheetAdded"), | 1000 StyleSheetAdded: Symbol('StyleSheetAdded'), |
| 72 StyleSheetChanged: Symbol("StyleSheetChanged"), | 1001 StyleSheetChanged: Symbol('StyleSheetChanged'), |
| 73 StyleSheetRemoved: Symbol("StyleSheetRemoved"), | 1002 StyleSheetRemoved: Symbol('StyleSheetRemoved'), |
| 74 SourceMapAttached: Symbol("SourceMapAttached"), | 1003 SourceMapAttached: Symbol('SourceMapAttached'), |
| 75 SourceMapDetached: Symbol("SourceMapDetached"), | 1004 SourceMapDetached: Symbol('SourceMapDetached'), |
| 76 SourceMapChanged: Symbol("SourceMapChanged") | 1005 SourceMapChanged: Symbol('SourceMapChanged') |
| 77 }; | 1006 }; |
| 78 | 1007 |
| 79 WebInspector.CSSModel.MediaTypes = ["all", "braille", "embossed", "handheld", "p
rint", "projection", "screen", "speech", "tty", "tv"]; | 1008 WebInspector.CSSModel.MediaTypes = |
| 80 | 1009 ['all', 'braille', 'embossed', 'handheld', 'print', 'projection', 'screen',
'speech', 'tty', 'tv']; |
| 81 WebInspector.CSSModel.PseudoStateMarker = "pseudo-state-marker"; | 1010 |
| 1011 WebInspector.CSSModel.PseudoStateMarker = 'pseudo-state-marker'; |
| 82 | 1012 |
| 83 /** | 1013 /** |
| 84 * @constructor | 1014 * @unrestricted |
| 85 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 86 * @param {!WebInspector.TextRange} oldRange | |
| 87 * @param {string} newText | |
| 88 * @param {?Object} payload | |
| 89 */ | 1015 */ |
| 90 WebInspector.CSSModel.Edit = function(styleSheetId, oldRange, newText, payload) | 1016 WebInspector.CSSModel.Edit = class { |
| 91 { | 1017 /** |
| 1018 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 1019 * @param {!WebInspector.TextRange} oldRange |
| 1020 * @param {string} newText |
| 1021 * @param {?Object} payload |
| 1022 */ |
| 1023 constructor(styleSheetId, oldRange, newText, payload) { |
| 92 this.styleSheetId = styleSheetId; | 1024 this.styleSheetId = styleSheetId; |
| 93 this.oldRange = oldRange; | 1025 this.oldRange = oldRange; |
| 94 this.newRange = WebInspector.TextRange.fromEdit(oldRange, newText); | 1026 this.newRange = WebInspector.TextRange.fromEdit(oldRange, newText); |
| 95 this.payload = payload; | 1027 this.payload = payload; |
| 1028 } |
| 96 }; | 1029 }; |
| 97 | 1030 |
| 98 WebInspector.CSSModel.prototype = { | |
| 99 /** | |
| 100 * @param {!WebInspector.Event} event | |
| 101 */ | |
| 102 _toggleSourceMapSupport: function(event) | |
| 103 { | |
| 104 var enabled = /** @type {boolean} */ (event.data); | |
| 105 var headers = this.styleSheetHeaders(); | |
| 106 for (var header of headers) { | |
| 107 if (enabled) | |
| 108 this._attachSourceMap(header); | |
| 109 else | |
| 110 this._detachSourceMap(header); | |
| 111 } | |
| 112 }, | |
| 113 | |
| 114 /** | |
| 115 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 116 * @return {?WebInspector.SourceMap} | |
| 117 */ | |
| 118 sourceMapForHeader: function(header) | |
| 119 { | |
| 120 return this._sourceMapByURL.get(header.sourceMapURL) || null; | |
| 121 }, | |
| 122 | |
| 123 _sourceMapLoadedForTest: function() { }, | |
| 124 | |
| 125 /** | |
| 126 * @param {!WebInspector.SourceMap} sourceMap | |
| 127 * @return {!Array<!WebInspector.CSSStyleSheetHeader>} | |
| 128 */ | |
| 129 headersForSourceMap: function(sourceMap) | |
| 130 { | |
| 131 return this._sourceMapURLToHeaders.get(sourceMap.url()).valuesArray(); | |
| 132 }, | |
| 133 | |
| 134 /** | |
| 135 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 136 */ | |
| 137 _attachSourceMap: function(header) | |
| 138 { | |
| 139 var sourceMapURL = header.sourceMapURL; | |
| 140 if (!sourceMapURL || !WebInspector.moduleSetting("cssSourceMapsEnabled")
.get()) | |
| 141 return; | |
| 142 if (this._sourceMapByURL.has(sourceMapURL)) { | |
| 143 attach.call(this, sourceMapURL, header); | |
| 144 return; | |
| 145 } | |
| 146 if (!this._sourceMapLoadingStyleSheetsIds.has(sourceMapURL)) { | |
| 147 WebInspector.TextSourceMap.load(sourceMapURL, header.sourceURL) | |
| 148 .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) | |
| 149 .then(onSourceMap.bind(this, sourceMapURL)); | |
| 150 } | |
| 151 this._sourceMapLoadingStyleSheetsIds.set(sourceMapURL, header.id); | |
| 152 | |
| 153 /** | |
| 154 * @param {string} sourceMapURL | |
| 155 * @param {?WebInspector.TextSourceMap} sourceMap | |
| 156 * @return {!Promise<?WebInspector.SourceMap>} | |
| 157 * @this {WebInspector.CSSModel} | |
| 158 */ | |
| 159 function onTextSourceMapLoaded(sourceMapURL, sourceMap) | |
| 160 { | |
| 161 if (!sourceMap) | |
| 162 return Promise.resolve(/** @type {?WebInspector.SourceMap} */(nu
ll)); | |
| 163 var factoryExtension = this._factoryForSourceMap(sourceMap); | |
| 164 if (!factoryExtension) | |
| 165 return Promise.resolve(/** @type {?WebInspector.SourceMap} */(so
urceMap)); | |
| 166 return factoryExtension.instance() | |
| 167 .then(factory => factory.editableSourceMap(this.target(), source
Map)) | |
| 168 .then(map => map || sourceMap) | |
| 169 .catchException(/** @type {?WebInspector.SourceMap} */(null)); | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * @param {string} sourceMapURL | |
| 174 * @param {?WebInspector.SourceMap} sourceMap | |
| 175 * @this {WebInspector.CSSModel} | |
| 176 */ | |
| 177 function onSourceMap(sourceMapURL, sourceMap) | |
| 178 { | |
| 179 this._sourceMapLoadedForTest(); | |
| 180 var styleSheetIds = this._sourceMapLoadingStyleSheetsIds.get(sourceM
apURL); | |
| 181 this._sourceMapLoadingStyleSheetsIds.removeAll(sourceMapURL); | |
| 182 if (!sourceMap) | |
| 183 return; | |
| 184 var headers = new Set(); | |
| 185 for (var styleSheetId of styleSheetIds) { | |
| 186 var header = this.styleSheetHeaderForId(styleSheetId); | |
| 187 if (header) | |
| 188 headers.add(header); | |
| 189 } | |
| 190 if (!headers.size) | |
| 191 return; | |
| 192 this._sourceMapByURL.set(sourceMapURL, sourceMap); | |
| 193 for (var header of headers) | |
| 194 attach.call(this, sourceMapURL, header); | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * @param {string} sourceMapURL | |
| 199 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 200 * @this {WebInspector.CSSModel} | |
| 201 */ | |
| 202 function attach(sourceMapURL, header) | |
| 203 { | |
| 204 this._sourceMapURLToHeaders.set(sourceMapURL, header); | |
| 205 this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMap
Attached, header); | |
| 206 } | |
| 207 }, | |
| 208 | |
| 209 /** | |
| 210 * @param {!WebInspector.SourceMap} sourceMap | |
| 211 * @return {?Runtime.Extension} | |
| 212 */ | |
| 213 _factoryForSourceMap: function(sourceMap) | |
| 214 { | |
| 215 var sourceExtensions = new Set(); | |
| 216 for (var url of sourceMap.sourceURLs()) | |
| 217 sourceExtensions.add(WebInspector.ParsedURL.extractExtension(url)); | |
| 218 for (var runtimeExtension of self.runtime.extensions(WebInspector.Source
MapFactory)) { | |
| 219 var supportedExtensions = new Set(runtimeExtension.descriptor()["ext
ensions"]); | |
| 220 if (supportedExtensions.containsAll(sourceExtensions)) | |
| 221 return runtimeExtension; | |
| 222 } | |
| 223 return null; | |
| 224 }, | |
| 225 | |
| 226 /** | |
| 227 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 228 */ | |
| 229 _detachSourceMap: function(header) | |
| 230 { | |
| 231 if (!header.sourceMapURL || !this._sourceMapURLToHeaders.hasValue(header
.sourceMapURL, header)) | |
| 232 return; | |
| 233 this._sourceMapURLToHeaders.remove(header.sourceMapURL, header); | |
| 234 if (!this._sourceMapURLToHeaders.has(header.sourceMapURL)) | |
| 235 this._sourceMapByURL.delete(header.sourceMapURL); | |
| 236 this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapDeta
ched, header); | |
| 237 }, | |
| 238 | |
| 239 /** | |
| 240 * @return {!WebInspector.DOMModel} | |
| 241 */ | |
| 242 domModel: function() | |
| 243 { | |
| 244 return this._domModel; | |
| 245 }, | |
| 246 | |
| 247 /** | |
| 248 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 249 * @param {!WebInspector.TextRange} range | |
| 250 * @param {string} text | |
| 251 * @param {boolean} majorChange | |
| 252 * @return {!Promise<boolean>} | |
| 253 */ | |
| 254 setStyleText: function(styleSheetId, range, text, majorChange) | |
| 255 { | |
| 256 var original = this._innerSetStyleTexts.bind(this, [styleSheetId], [rang
e], [text], majorChange); | |
| 257 var header = this.styleSheetHeaderForId(styleSheetId); | |
| 258 if (!header) | |
| 259 return original(); | |
| 260 | |
| 261 var sourceMap = this.sourceMapForHeader(header); | |
| 262 if (!sourceMap) | |
| 263 return original(); | |
| 264 | |
| 265 var originalAndDetach = originalAndDetachIfSuccess.bind(this, header); | |
| 266 | |
| 267 if (!sourceMap.editable()) | |
| 268 return original(); | |
| 269 | |
| 270 return /** @type {!Promise<boolean>} */(sourceMap.editCompiled([range],
[text]) | |
| 271 .then(onEditingDone.bind(this)) | |
| 272 .catch(onError.bind(this, header))); | |
| 273 | |
| 274 /** | |
| 275 * @param {?WebInspector.SourceMap.EditResult} editResult | |
| 276 * @return {!Promise<boolean>} | |
| 277 * @this {WebInspector.CSSModel} | |
| 278 */ | |
| 279 function onEditingDone(editResult) | |
| 280 { | |
| 281 if (!editResult) | |
| 282 return Promise.resolve(false); | |
| 283 | |
| 284 var edits = editResult.compiledEdits; | |
| 285 if (!edits.length) | |
| 286 return onCSSPatched.call(this, editResult, true); | |
| 287 | |
| 288 edits.sort(WebInspector.SourceEdit.comparator); | |
| 289 edits = edits.reverse(); | |
| 290 | |
| 291 var styleSheetIds = []; | |
| 292 var ranges = []; | |
| 293 var texts = []; | |
| 294 for (var edit of edits) { | |
| 295 styleSheetIds.push(header.id); | |
| 296 ranges.push(edit.oldRange); | |
| 297 texts.push(edit.newText); | |
| 298 } | |
| 299 return this._innerSetStyleTexts(styleSheetIds, ranges, texts, majorC
hange) | |
| 300 .then(onCSSPatched.bind(this, editResult)); | |
| 301 } | |
| 302 | |
| 303 /** | |
| 304 * @param {!WebInspector.SourceMap.EditResult} editResult | |
| 305 * @param {boolean} success | |
| 306 * @return {!Promise<boolean>} | |
| 307 * @this {WebInspector.CSSModel} | |
| 308 */ | |
| 309 function onCSSPatched(editResult, success) | |
| 310 { | |
| 311 if (!success) | |
| 312 return originalAndDetach(); | |
| 313 | |
| 314 this._sourceMapByURL.set(header.sourceMapURL, editResult.map); | |
| 315 this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMap
Changed, { | |
| 316 sourceMap: editResult.map, | |
| 317 newSources: editResult.newSources | |
| 318 }); | |
| 319 return Promise.resolve(true); | |
| 320 } | |
| 321 | |
| 322 /** | |
| 323 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 324 * @param {*} error | |
| 325 * @return {!Promise<boolean>} | |
| 326 * @this {WebInspector.CSSModel} | |
| 327 */ | |
| 328 function onError(header, error) | |
| 329 { | |
| 330 WebInspector.console.error(WebInspector.UIString("LiveSASS failed: %
s", sourceMap.compiledURL())); | |
| 331 console.error(error); | |
| 332 this._detachSourceMap(header); | |
| 333 return original(); | |
| 334 } | |
| 335 | |
| 336 /** | |
| 337 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 338 * @return {!Promise<boolean>} | |
| 339 * @this {WebInspector.CSSModel} | |
| 340 */ | |
| 341 function originalAndDetachIfSuccess(header) | |
| 342 { | |
| 343 return this._innerSetStyleTexts([styleSheetId], [range], [text], maj
orChange) | |
| 344 .then(detachIfSuccess.bind(this)); | |
| 345 | |
| 346 /** | |
| 347 * @param {boolean} success | |
| 348 * @return {boolean} | |
| 349 * @this {WebInspector.CSSModel} | |
| 350 */ | |
| 351 function detachIfSuccess(success) | |
| 352 { | |
| 353 if (success) | |
| 354 this._detachSourceMap(header); | |
| 355 return success; | |
| 356 } | |
| 357 } | |
| 358 }, | |
| 359 | |
| 360 /** | |
| 361 * @param {!Array<!CSSAgent.StyleSheetId>} styleSheetIds | |
| 362 * @param {!Array<!WebInspector.TextRange>} ranges | |
| 363 * @param {!Array<string>} texts | |
| 364 * @param {boolean} majorChange | |
| 365 * @return {!Promise<boolean>} | |
| 366 */ | |
| 367 _innerSetStyleTexts: function(styleSheetIds, ranges, texts, majorChange) | |
| 368 { | |
| 369 /** | |
| 370 * @param {?Protocol.Error} error | |
| 371 * @param {?Array<!CSSAgent.CSSStyle>} stylePayloads | |
| 372 * @return {boolean} | |
| 373 * @this {WebInspector.CSSModel} | |
| 374 */ | |
| 375 function parsePayload(error, stylePayloads) | |
| 376 { | |
| 377 if (error || !stylePayloads || stylePayloads.length !== ranges.lengt
h) | |
| 378 return false; | |
| 379 | |
| 380 if (majorChange) | |
| 381 this._domModel.markUndoableState(); | |
| 382 for (var i = 0; i < ranges.length; ++i) { | |
| 383 var edit = new WebInspector.CSSModel.Edit(styleSheetIds[i], rang
es[i], texts[i], stylePayloads[i]); | |
| 384 this._fireStyleSheetChanged(styleSheetIds[i], edit); | |
| 385 } | |
| 386 return true; | |
| 387 } | |
| 388 | |
| 389 console.assert(styleSheetIds.length === ranges.length && ranges.length =
== texts.length, "Array lengths must be equal"); | |
| 390 var edits = []; | |
| 391 var ensureContentPromises = []; | |
| 392 for (var i = 0; i < styleSheetIds.length; ++i) { | |
| 393 edits.push({ | |
| 394 styleSheetId: styleSheetIds[i], | |
| 395 range: ranges[i].serializeToObject(), | |
| 396 text: texts[i] | |
| 397 }); | |
| 398 ensureContentPromises.push(this._ensureOriginalStyleSheetText(styleS
heetIds[i])); | |
| 399 } | |
| 400 | |
| 401 return Promise.all(ensureContentPromises) | |
| 402 .then(() => this._agent.setStyleTexts(edits, parsePayload.bind(this)
)) | |
| 403 .catchException(false); | |
| 404 }, | |
| 405 | |
| 406 /** | |
| 407 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 408 * @param {!WebInspector.TextRange} range | |
| 409 * @param {string} text | |
| 410 * @return {!Promise<boolean>} | |
| 411 */ | |
| 412 setSelectorText: function(styleSheetId, range, text) | |
| 413 { | |
| 414 /** | |
| 415 * @param {?Protocol.Error} error | |
| 416 * @param {?CSSAgent.SelectorList} selectorPayload | |
| 417 * @return {boolean} | |
| 418 * @this {WebInspector.CSSModel} | |
| 419 */ | |
| 420 function callback(error, selectorPayload) | |
| 421 { | |
| 422 if (error || !selectorPayload) | |
| 423 return false; | |
| 424 this._domModel.markUndoableState(); | |
| 425 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text,
selectorPayload); | |
| 426 this._fireStyleSheetChanged(styleSheetId, edit); | |
| 427 return true; | |
| 428 } | |
| 429 | |
| 430 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.Sty
leRuleEdited); | |
| 431 return this._ensureOriginalStyleSheetText(styleSheetId) | |
| 432 .then(() => this._agent.setRuleSelector(styleSheetId, range, text, c
allback.bind(this))) | |
| 433 .catchException(false); | |
| 434 }, | |
| 435 | |
| 436 /** | |
| 437 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 438 * @param {!WebInspector.TextRange} range | |
| 439 * @param {string} text | |
| 440 * @return {!Promise<boolean>} | |
| 441 */ | |
| 442 setKeyframeKey: function(styleSheetId, range, text) | |
| 443 { | |
| 444 /** | |
| 445 * @param {?Protocol.Error} error | |
| 446 * @param {!CSSAgent.Value} payload | |
| 447 * @return {boolean} | |
| 448 * @this {WebInspector.CSSModel} | |
| 449 */ | |
| 450 function callback(error, payload) | |
| 451 { | |
| 452 if (error || !payload) | |
| 453 return false; | |
| 454 this._domModel.markUndoableState(); | |
| 455 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text,
payload); | |
| 456 this._fireStyleSheetChanged(styleSheetId, edit); | |
| 457 return true; | |
| 458 } | |
| 459 | |
| 460 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.Sty
leRuleEdited); | |
| 461 return this._ensureOriginalStyleSheetText(styleSheetId) | |
| 462 .then(() => this._agent.setKeyframeKey(styleSheetId, range, text, ca
llback.bind(this))) | |
| 463 .catchException(false); | |
| 464 }, | |
| 465 | |
| 466 /** | |
| 467 * @return {!Promise.<!Array.<!WebInspector.CSSMedia>>} | |
| 468 */ | |
| 469 mediaQueriesPromise: function() | |
| 470 { | |
| 471 /** | |
| 472 * @param {?Protocol.Error} error | |
| 473 * @param {?Array.<!CSSAgent.CSSMedia>} payload | |
| 474 * @return {!Array.<!WebInspector.CSSMedia>} | |
| 475 * @this {!WebInspector.CSSModel} | |
| 476 */ | |
| 477 function parsePayload(error, payload) | |
| 478 { | |
| 479 return !error && payload ? WebInspector.CSSMedia.parseMediaArrayPayl
oad(this, payload) : []; | |
| 480 } | |
| 481 | |
| 482 return this._agent.getMediaQueries(parsePayload.bind(this)); | |
| 483 }, | |
| 484 | |
| 485 /** | |
| 486 * @return {boolean} | |
| 487 */ | |
| 488 isEnabled: function() | |
| 489 { | |
| 490 return this._isEnabled; | |
| 491 }, | |
| 492 | |
| 493 /** | |
| 494 * @param {?Protocol.Error} error | |
| 495 */ | |
| 496 _wasEnabled: function(error) | |
| 497 { | |
| 498 if (error) { | |
| 499 console.error("Failed to enabled CSS agent: " + error); | |
| 500 return; | |
| 501 } | |
| 502 this._isEnabled = true; | |
| 503 this.dispatchEventToListeners(WebInspector.CSSModel.Events.ModelWasEnabl
ed); | |
| 504 }, | |
| 505 | |
| 506 /** | |
| 507 * @param {!DOMAgent.NodeId} nodeId | |
| 508 * @return {!Promise.<?WebInspector.CSSMatchedStyles>} | |
| 509 */ | |
| 510 matchedStylesPromise: function(nodeId) | |
| 511 { | |
| 512 /** | |
| 513 * @param {?Protocol.Error} error | |
| 514 * @param {?CSSAgent.CSSStyle=} inlinePayload | |
| 515 * @param {?CSSAgent.CSSStyle=} attributesPayload | |
| 516 * @param {!Array.<!CSSAgent.RuleMatch>=} matchedPayload | |
| 517 * @param {!Array.<!CSSAgent.PseudoElementMatches>=} pseudoPayload | |
| 518 * @param {!Array.<!CSSAgent.InheritedStyleEntry>=} inheritedPayload | |
| 519 * @param {!Array.<!CSSAgent.CSSKeyframesRule>=} animationsPayload | |
| 520 * @return {?WebInspector.CSSMatchedStyles} | |
| 521 * @this {WebInspector.CSSModel} | |
| 522 */ | |
| 523 function callback(error, inlinePayload, attributesPayload, matchedPayloa
d, pseudoPayload, inheritedPayload, animationsPayload) | |
| 524 { | |
| 525 if (error) | |
| 526 return null; | |
| 527 | |
| 528 var node = this._domModel.nodeForId(nodeId); | |
| 529 if (!node) | |
| 530 return null; | |
| 531 | |
| 532 return new WebInspector.CSSMatchedStyles(this, node, inlinePayload |
| null, attributesPayload || null, matchedPayload || [], pseudoPayload || [], in
heritedPayload || [], animationsPayload || []); | |
| 533 } | |
| 534 | |
| 535 return this._agent.getMatchedStylesForNode(nodeId, callback.bind(this)); | |
| 536 }, | |
| 537 | |
| 538 /** | |
| 539 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 540 * @return {!Promise<!Array<string>>} | |
| 541 */ | |
| 542 classNamesPromise: function(styleSheetId) | |
| 543 { | |
| 544 /** | |
| 545 * @param {?string} error | |
| 546 * @param {?Array<string>} classNames | |
| 547 * @return {!Array<string>} | |
| 548 */ | |
| 549 function classNamesCallback(error, classNames) | |
| 550 { | |
| 551 return !error && classNames ? classNames : []; | |
| 552 } | |
| 553 return this._agent.collectClassNames(styleSheetId, classNamesCallback); | |
| 554 }, | |
| 555 | |
| 556 /** | |
| 557 * @param {!DOMAgent.NodeId} nodeId | |
| 558 * @return {!Promise.<?Map.<string, string>>} | |
| 559 */ | |
| 560 computedStylePromise: function(nodeId) | |
| 561 { | |
| 562 return this._styleLoader.computedStylePromise(nodeId); | |
| 563 }, | |
| 564 | |
| 565 /** | |
| 566 * @param {number} nodeId | |
| 567 * @return {!Promise<?Array<string>>} | |
| 568 */ | |
| 569 backgroundColorsPromise: function(nodeId) | |
| 570 { | |
| 571 /** | |
| 572 * @param {?string} error | |
| 573 * @param {!Array<string>=} backgroundColors | |
| 574 * @return {?Array<string>} | |
| 575 */ | |
| 576 function backgroundColorsCallback(error, backgroundColors) { | |
| 577 return !error && backgroundColors ? backgroundColors : null; | |
| 578 } | |
| 579 return this._agent.getBackgroundColors(nodeId, backgroundColorsCallback)
; | |
| 580 }, | |
| 581 | |
| 582 /** | |
| 583 * @param {number} nodeId | |
| 584 * @return {!Promise.<?Array.<!CSSAgent.PlatformFontUsage>>} | |
| 585 */ | |
| 586 platformFontsPromise: function(nodeId) | |
| 587 { | |
| 588 /** | |
| 589 * @param {?Protocol.Error} error | |
| 590 * @param {?Array.<!CSSAgent.PlatformFontUsage>} fonts | |
| 591 * @return {?Array.<!CSSAgent.PlatformFontUsage>} | |
| 592 */ | |
| 593 function platformFontsCallback(error, fonts) | |
| 594 { | |
| 595 return !error && fonts ? fonts : null; | |
| 596 } | |
| 597 | |
| 598 return this._agent.getPlatformFontsForNode(nodeId, platformFontsCallback
); | |
| 599 }, | |
| 600 | |
| 601 /** | |
| 602 * @return {!Array.<!WebInspector.CSSStyleSheetHeader>} | |
| 603 */ | |
| 604 allStyleSheets: function() | |
| 605 { | |
| 606 var values = this._styleSheetIdToHeader.valuesArray(); | |
| 607 /** | |
| 608 * @param {!WebInspector.CSSStyleSheetHeader} a | |
| 609 * @param {!WebInspector.CSSStyleSheetHeader} b | |
| 610 * @return {number} | |
| 611 */ | |
| 612 function styleSheetComparator(a, b) | |
| 613 { | |
| 614 if (a.sourceURL < b.sourceURL) | |
| 615 return -1; | |
| 616 else if (a.sourceURL > b.sourceURL) | |
| 617 return 1; | |
| 618 return a.startLine - b.startLine || a.startColumn - b.startColumn; | |
| 619 } | |
| 620 values.sort(styleSheetComparator); | |
| 621 | |
| 622 return values; | |
| 623 }, | |
| 624 | |
| 625 /** | |
| 626 * @param {!DOMAgent.NodeId} nodeId | |
| 627 * @return {!Promise.<?WebInspector.CSSModel.InlineStyleResult>} | |
| 628 */ | |
| 629 inlineStylesPromise: function(nodeId) | |
| 630 { | |
| 631 /** | |
| 632 * @param {?Protocol.Error} error | |
| 633 * @param {?CSSAgent.CSSStyle=} inlinePayload | |
| 634 * @param {?CSSAgent.CSSStyle=} attributesStylePayload | |
| 635 * @return {?WebInspector.CSSModel.InlineStyleResult} | |
| 636 * @this {WebInspector.CSSModel} | |
| 637 */ | |
| 638 function callback(error, inlinePayload, attributesStylePayload) | |
| 639 { | |
| 640 if (error || !inlinePayload) | |
| 641 return null; | |
| 642 var inlineStyle = inlinePayload ? new WebInspector.CSSStyleDeclarati
on(this, null, inlinePayload, WebInspector.CSSStyleDeclaration.Type.Inline) : nu
ll; | |
| 643 var attributesStyle = attributesStylePayload ? new WebInspector.CSSS
tyleDeclaration(this, null, attributesStylePayload, WebInspector.CSSStyleDeclara
tion.Type.Attributes) : null; | |
| 644 return new WebInspector.CSSModel.InlineStyleResult(inlineStyle, attr
ibutesStyle); | |
| 645 } | |
| 646 | |
| 647 return this._agent.getInlineStylesForNode(nodeId, callback.bind(this)); | |
| 648 }, | |
| 649 | |
| 650 /** | |
| 651 * @param {!WebInspector.DOMNode} node | |
| 652 * @param {string} pseudoClass | |
| 653 * @param {boolean} enable | |
| 654 * @return {boolean} | |
| 655 */ | |
| 656 forcePseudoState: function(node, pseudoClass, enable) | |
| 657 { | |
| 658 var pseudoClasses = node.marker(WebInspector.CSSModel.PseudoStateMarker)
|| []; | |
| 659 if (enable) { | |
| 660 if (pseudoClasses.indexOf(pseudoClass) >= 0) | |
| 661 return false; | |
| 662 pseudoClasses.push(pseudoClass); | |
| 663 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, pseudoClasse
s); | |
| 664 } else { | |
| 665 if (pseudoClasses.indexOf(pseudoClass) < 0) | |
| 666 return false; | |
| 667 pseudoClasses.remove(pseudoClass); | |
| 668 if (pseudoClasses.length) | |
| 669 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, pseudoCl
asses); | |
| 670 else | |
| 671 node.setMarker(WebInspector.CSSModel.PseudoStateMarker, null); | |
| 672 } | |
| 673 | |
| 674 this._agent.forcePseudoState(node.id, pseudoClasses); | |
| 675 this.dispatchEventToListeners(WebInspector.CSSModel.Events.PseudoStateFo
rced, { node: node, pseudoClass: pseudoClass, enable: enable }); | |
| 676 return true; | |
| 677 }, | |
| 678 | |
| 679 /** | |
| 680 * @param {!WebInspector.DOMNode} node | |
| 681 * @return {?Array<string>} state | |
| 682 */ | |
| 683 pseudoState: function(node) | |
| 684 { | |
| 685 return node.marker(WebInspector.CSSModel.PseudoStateMarker) || []; | |
| 686 }, | |
| 687 | |
| 688 /** | |
| 689 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 690 * @param {!WebInspector.TextRange} range | |
| 691 * @param {string} newMediaText | |
| 692 * @return {!Promise<boolean>} | |
| 693 */ | |
| 694 setMediaText: function(styleSheetId, range, newMediaText) | |
| 695 { | |
| 696 /** | |
| 697 * @param {?Protocol.Error} error | |
| 698 * @param {!CSSAgent.CSSMedia} mediaPayload | |
| 699 * @return {boolean} | |
| 700 * @this {WebInspector.CSSModel} | |
| 701 */ | |
| 702 function parsePayload(error, mediaPayload) | |
| 703 { | |
| 704 if (!mediaPayload) | |
| 705 return false; | |
| 706 this._domModel.markUndoableState(); | |
| 707 var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, newMe
diaText, mediaPayload); | |
| 708 this._fireStyleSheetChanged(styleSheetId, edit); | |
| 709 return true; | |
| 710 } | |
| 711 | |
| 712 WebInspector.userMetrics.actionTaken(WebInspector.UserMetrics.Action.Sty
leRuleEdited); | |
| 713 return this._ensureOriginalStyleSheetText(styleSheetId) | |
| 714 .then(() => this._agent.setMediaText(styleSheetId, range, newMediaTe
xt, parsePayload.bind(this))) | |
| 715 .catchException(false); | |
| 716 }, | |
| 717 | |
| 718 /** | |
| 719 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 720 * @param {string} ruleText | |
| 721 * @param {!WebInspector.TextRange} ruleLocation | |
| 722 * @return {!Promise<?WebInspector.CSSStyleRule>} | |
| 723 */ | |
| 724 addRule: function(styleSheetId, ruleText, ruleLocation) | |
| 725 { | |
| 726 return this._ensureOriginalStyleSheetText(styleSheetId) | |
| 727 .then(() => this._agent.addRule(styleSheetId, ruleText, ruleLocation
, parsePayload.bind(this))) | |
| 728 .catchException(/** @type {?WebInspector.CSSStyleRule} */(null)); | |
| 729 | |
| 730 /** | |
| 731 * @param {?Protocol.Error} error | |
| 732 * @param {?CSSAgent.CSSRule} rulePayload | |
| 733 * @return {?WebInspector.CSSStyleRule} | |
| 734 * @this {WebInspector.CSSModel} | |
| 735 */ | |
| 736 function parsePayload(error, rulePayload) | |
| 737 { | |
| 738 if (error || !rulePayload) | |
| 739 return null; | |
| 740 this._domModel.markUndoableState(); | |
| 741 var edit = new WebInspector.CSSModel.Edit(styleSheetId, ruleLocation
, ruleText, rulePayload); | |
| 742 this._fireStyleSheetChanged(styleSheetId, edit); | |
| 743 return new WebInspector.CSSStyleRule(this, rulePayload); | |
| 744 } | |
| 745 }, | |
| 746 | |
| 747 /** | |
| 748 * @param {!WebInspector.DOMNode} node | |
| 749 * @param {function(?WebInspector.CSSStyleSheetHeader)} userCallback | |
| 750 */ | |
| 751 requestViaInspectorStylesheet: function(node, userCallback) | |
| 752 { | |
| 753 var frameId = node.frameId() || WebInspector.ResourceTreeModel.fromTarge
t(this.target()).mainFrame.id; | |
| 754 var headers = this._styleSheetIdToHeader.valuesArray(); | |
| 755 for (var i = 0; i < headers.length; ++i) { | |
| 756 var styleSheetHeader = headers[i]; | |
| 757 if (styleSheetHeader.frameId === frameId && styleSheetHeader.isViaIn
spector()) { | |
| 758 userCallback(styleSheetHeader); | |
| 759 return; | |
| 760 } | |
| 761 } | |
| 762 | |
| 763 /** | |
| 764 * @param {?Protocol.Error} error | |
| 765 * @param {?CSSAgent.StyleSheetId} styleSheetId | |
| 766 * @return {?WebInspector.CSSStyleSheetHeader} | |
| 767 * @this {WebInspector.CSSModel} | |
| 768 */ | |
| 769 function innerCallback(error, styleSheetId) | |
| 770 { | |
| 771 return !error && styleSheetId ? this._styleSheetIdToHeader.get(style
SheetId) || null : null; | |
| 772 } | |
| 773 | |
| 774 this._agent.createStyleSheet(frameId, innerCallback.bind(this)) | |
| 775 .catchException(null) | |
| 776 .then(userCallback); | |
| 777 }, | |
| 778 | |
| 779 mediaQueryResultChanged: function() | |
| 780 { | |
| 781 this.dispatchEventToListeners(WebInspector.CSSModel.Events.MediaQueryRes
ultChanged); | |
| 782 }, | |
| 783 | |
| 784 fontsUpdated: function() | |
| 785 { | |
| 786 this.dispatchEventToListeners(WebInspector.CSSModel.Events.FontsUpdated)
; | |
| 787 }, | |
| 788 | |
| 789 /** | |
| 790 * @param {!CSSAgent.StyleSheetId} id | |
| 791 * @return {?WebInspector.CSSStyleSheetHeader} | |
| 792 */ | |
| 793 styleSheetHeaderForId: function(id) | |
| 794 { | |
| 795 return this._styleSheetIdToHeader.get(id) || null; | |
| 796 }, | |
| 797 | |
| 798 /** | |
| 799 * @return {!Array.<!WebInspector.CSSStyleSheetHeader>} | |
| 800 */ | |
| 801 styleSheetHeaders: function() | |
| 802 { | |
| 803 return this._styleSheetIdToHeader.valuesArray(); | |
| 804 }, | |
| 805 | |
| 806 /** | |
| 807 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 808 * @param {!WebInspector.CSSModel.Edit=} edit | |
| 809 */ | |
| 810 _fireStyleSheetChanged: function(styleSheetId, edit) | |
| 811 { | |
| 812 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetCha
nged, { styleSheetId: styleSheetId, edit: edit }); | |
| 813 }, | |
| 814 | |
| 815 /** | |
| 816 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 817 * @return {!Promise<string>} | |
| 818 */ | |
| 819 _ensureOriginalStyleSheetText: function(styleSheetId) | |
| 820 { | |
| 821 var header = this.styleSheetHeaderForId(styleSheetId); | |
| 822 if (!header) | |
| 823 return Promise.resolve(""); | |
| 824 var promise = this._originalStyleSheetText.get(header); | |
| 825 if (!promise) { | |
| 826 promise = this.getStyleSheetText(header.id); | |
| 827 this._originalStyleSheetText.set(header, promise); | |
| 828 this._originalContentRequestedForTest(header); | |
| 829 } | |
| 830 return promise; | |
| 831 }, | |
| 832 | |
| 833 /** | |
| 834 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 835 */ | |
| 836 _originalContentRequestedForTest: function(header) { }, | |
| 837 | |
| 838 /** | |
| 839 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 840 * @return {!Promise<string>} | |
| 841 */ | |
| 842 originalStyleSheetText: function(header) | |
| 843 { | |
| 844 return this._ensureOriginalStyleSheetText(header.id); | |
| 845 }, | |
| 846 | |
| 847 /** | |
| 848 * @param {!CSSAgent.CSSStyleSheetHeader} header | |
| 849 */ | |
| 850 _styleSheetAdded: function(header) | |
| 851 { | |
| 852 console.assert(!this._styleSheetIdToHeader.get(header.styleSheetId)); | |
| 853 var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(this, header
); | |
| 854 this._styleSheetIdToHeader.set(header.styleSheetId, styleSheetHeader); | |
| 855 var url = styleSheetHeader.resourceURL(); | |
| 856 if (!this._styleSheetIdsForURL.get(url)) | |
| 857 this._styleSheetIdsForURL.set(url, {}); | |
| 858 var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(url); | |
| 859 var styleSheetIds = frameIdToStyleSheetIds[styleSheetHeader.frameId]; | |
| 860 if (!styleSheetIds) { | |
| 861 styleSheetIds = []; | |
| 862 frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds; | |
| 863 } | |
| 864 styleSheetIds.push(styleSheetHeader.id); | |
| 865 this._attachSourceMap(styleSheetHeader); | |
| 866 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetAdd
ed, styleSheetHeader); | |
| 867 }, | |
| 868 | |
| 869 /** | |
| 870 * @param {!CSSAgent.StyleSheetId} id | |
| 871 */ | |
| 872 _styleSheetRemoved: function(id) | |
| 873 { | |
| 874 var header = this._styleSheetIdToHeader.get(id); | |
| 875 console.assert(header); | |
| 876 if (!header) | |
| 877 return; | |
| 878 this._styleSheetIdToHeader.remove(id); | |
| 879 var url = header.resourceURL(); | |
| 880 var frameIdToStyleSheetIds = /** @type {!Object.<!PageAgent.FrameId, !Ar
ray.<!CSSAgent.StyleSheetId>>} */ (this._styleSheetIdsForURL.get(url)); | |
| 881 console.assert(frameIdToStyleSheetIds, "No frameId to styleSheetId map i
s available for given style sheet URL."); | |
| 882 frameIdToStyleSheetIds[header.frameId].remove(id); | |
| 883 if (!frameIdToStyleSheetIds[header.frameId].length) { | |
| 884 delete frameIdToStyleSheetIds[header.frameId]; | |
| 885 if (!Object.keys(frameIdToStyleSheetIds).length) | |
| 886 this._styleSheetIdsForURL.remove(url); | |
| 887 } | |
| 888 this._originalStyleSheetText.remove(header); | |
| 889 this._detachSourceMap(header); | |
| 890 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetRem
oved, header); | |
| 891 }, | |
| 892 | |
| 893 /** | |
| 894 * @param {string} url | |
| 895 * @return {!Array.<!CSSAgent.StyleSheetId>} | |
| 896 */ | |
| 897 styleSheetIdsForURL: function(url) | |
| 898 { | |
| 899 var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(url); | |
| 900 if (!frameIdToStyleSheetIds) | |
| 901 return []; | |
| 902 | |
| 903 var result = []; | |
| 904 for (var frameId in frameIdToStyleSheetIds) | |
| 905 result = result.concat(frameIdToStyleSheetIds[frameId]); | |
| 906 return result; | |
| 907 }, | |
| 908 | |
| 909 /** | |
| 910 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 911 * @param {string} newText | |
| 912 * @param {boolean} majorChange | |
| 913 * @return {!Promise.<?Protocol.Error>} | |
| 914 */ | |
| 915 setStyleSheetText: function(styleSheetId, newText, majorChange) | |
| 916 { | |
| 917 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */(this._styl
eSheetIdToHeader.get(styleSheetId)); | |
| 918 console.assert(header); | |
| 919 newText = WebInspector.CSSModel.trimSourceURL(newText); | |
| 920 if (header.hasSourceURL) | |
| 921 newText += "\n/*# sourceURL=" + header.sourceURL + " */"; | |
| 922 return this._ensureOriginalStyleSheetText(styleSheetId) | |
| 923 .then(() => this._agent.setStyleSheetText(header.id, newText, callba
ck.bind(this))); | |
| 924 | |
| 925 /** | |
| 926 * @param {?Protocol.Error} error | |
| 927 * @param {string=} sourceMapURL | |
| 928 * @return {?Protocol.Error} | |
| 929 * @this {WebInspector.CSSModel} | |
| 930 */ | |
| 931 function callback(error, sourceMapURL) | |
| 932 { | |
| 933 this._detachSourceMap(header); | |
| 934 header.setSourceMapURL(sourceMapURL); | |
| 935 this._attachSourceMap(header); | |
| 936 if (error) | |
| 937 return error; | |
| 938 if (majorChange) | |
| 939 this._domModel.markUndoableState(); | |
| 940 this._fireStyleSheetChanged(styleSheetId); | |
| 941 return null; | |
| 942 } | |
| 943 }, | |
| 944 | |
| 945 /** | |
| 946 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 947 * @return {!Promise<string>} | |
| 948 */ | |
| 949 getStyleSheetText: function(styleSheetId) | |
| 950 { | |
| 951 /** | |
| 952 * @param {?Protocol.Error} error | |
| 953 * @param {?string} text | |
| 954 * @return {string} | |
| 955 */ | |
| 956 function textCallback(error, text) | |
| 957 { | |
| 958 if (error || text === null) { | |
| 959 console.error("Failed to get text for stylesheet " + styleSheetI
d + ": " + error); | |
| 960 text = ""; | |
| 961 // Fall through. | |
| 962 } | |
| 963 return WebInspector.CSSModel.trimSourceURL(text); | |
| 964 } | |
| 965 | |
| 966 return this._agent.getStyleSheetText(styleSheetId, textCallback) | |
| 967 .catchException(/** @type {string} */("")); | |
| 968 }, | |
| 969 | |
| 970 /** | |
| 971 * @param {!WebInspector.Event} event | |
| 972 */ | |
| 973 _mainFrameNavigated: function(event) | |
| 974 { | |
| 975 if (event.data.target() !== this.target()) | |
| 976 return; | |
| 977 this._resetStyleSheets(); | |
| 978 }, | |
| 979 | |
| 980 _resetStyleSheets: function() | |
| 981 { | |
| 982 var headers = this._styleSheetIdToHeader.valuesArray(); | |
| 983 this._styleSheetIdsForURL.clear(); | |
| 984 this._styleSheetIdToHeader.clear(); | |
| 985 for (var i = 0; i < headers.length; ++i) { | |
| 986 this._detachSourceMap(headers[i]); | |
| 987 this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleShee
tRemoved, headers[i]); | |
| 988 } | |
| 989 this._sourceMapByURL.clear(); | |
| 990 this._sourceMapURLToHeaders.clear(); | |
| 991 this._sourceMapLoadingStyleSheetsIds.clear(); | |
| 992 }, | |
| 993 | |
| 994 /** | |
| 995 * @override | |
| 996 * @return {!Promise} | |
| 997 */ | |
| 998 suspendModel: function() | |
| 999 { | |
| 1000 this._isEnabled = false; | |
| 1001 return this._agent.disable().then(this._resetStyleSheets.bind(this)); | |
| 1002 }, | |
| 1003 | |
| 1004 /** | |
| 1005 * @override | |
| 1006 * @return {!Promise} | |
| 1007 */ | |
| 1008 resumeModel: function() | |
| 1009 { | |
| 1010 return this._agent.enable().then(this._wasEnabled.bind(this)); | |
| 1011 }, | |
| 1012 | |
| 1013 /** | |
| 1014 * @param {!CSSAgent.StyleSheetId} id | |
| 1015 * @param {!CSSAgent.SourceRange} range | |
| 1016 */ | |
| 1017 _layoutEditorChange: function(id, range) | |
| 1018 { | |
| 1019 this.dispatchEventToListeners(WebInspector.CSSModel.Events.LayoutEditorC
hange, {id: id, range: range}); | |
| 1020 }, | |
| 1021 | |
| 1022 /** | |
| 1023 * @param {number} nodeId | |
| 1024 * @param {string} name | |
| 1025 * @param {string} value | |
| 1026 */ | |
| 1027 setEffectivePropertyValueForNode: function(nodeId, name, value) | |
| 1028 { | |
| 1029 this._agent.setEffectivePropertyValueForNode(nodeId, name, value); | |
| 1030 }, | |
| 1031 | |
| 1032 /** | |
| 1033 * @param {!WebInspector.DOMNode} node | |
| 1034 * @return {!Promise.<?WebInspector.CSSMatchedStyles>} | |
| 1035 */ | |
| 1036 cachedMatchedCascadeForNode: function(node) | |
| 1037 { | |
| 1038 if (this._cachedMatchedCascadeNode !== node) | |
| 1039 this.discardCachedMatchedCascade(); | |
| 1040 this._cachedMatchedCascadeNode = node; | |
| 1041 if (!this._cachedMatchedCascadePromise) | |
| 1042 this._cachedMatchedCascadePromise = this.matchedStylesPromise(node.i
d); | |
| 1043 return this._cachedMatchedCascadePromise; | |
| 1044 }, | |
| 1045 | |
| 1046 discardCachedMatchedCascade: function() | |
| 1047 { | |
| 1048 delete this._cachedMatchedCascadeNode; | |
| 1049 delete this._cachedMatchedCascadePromise; | |
| 1050 }, | |
| 1051 | |
| 1052 __proto__: WebInspector.SDKModel.prototype | |
| 1053 }; | |
| 1054 | 1031 |
| 1055 /** | 1032 /** |
| 1056 * @param {string} text | 1033 * @unrestricted |
| 1057 * @return {string} | |
| 1058 */ | 1034 */ |
| 1059 WebInspector.CSSModel.trimSourceURL = function(text) | 1035 WebInspector.CSSLocation = class extends WebInspector.SDKObject { |
| 1060 { | 1036 /** |
| 1061 var sourceURLIndex = text.lastIndexOf("/*# sourceURL="); | 1037 * @param {!WebInspector.CSSStyleSheetHeader} header |
| 1062 if (sourceURLIndex === -1) { | 1038 * @param {number} lineNumber |
| 1063 sourceURLIndex = text.lastIndexOf("/*@ sourceURL="); | 1039 * @param {number=} columnNumber |
| 1064 if (sourceURLIndex === -1) | 1040 */ |
| 1065 return text; | 1041 constructor(header, lineNumber, columnNumber) { |
| 1066 } | 1042 super(header.target()); |
| 1067 var sourceURLLineIndex = text.lastIndexOf("\n", sourceURLIndex); | |
| 1068 if (sourceURLLineIndex === -1) | |
| 1069 return text; | |
| 1070 var sourceURLLine = text.substr(sourceURLLineIndex + 1).split("\n", 1)[0]; | |
| 1071 var sourceURLRegex = /[\040\t]*\/\*[#@] sourceURL=[\040\t]*([^\s]*)[\040\t]*
\*\/[\040\t]*$/; | |
| 1072 if (sourceURLLine.search(sourceURLRegex) === -1) | |
| 1073 return text; | |
| 1074 return text.substr(0, sourceURLLineIndex) + text.substr(sourceURLLineIndex +
sourceURLLine.length + 1); | |
| 1075 }; | |
| 1076 | |
| 1077 | |
| 1078 /** | |
| 1079 * @constructor | |
| 1080 * @extends {WebInspector.SDKObject} | |
| 1081 * @param {!WebInspector.CSSStyleSheetHeader} header | |
| 1082 * @param {number} lineNumber | |
| 1083 * @param {number=} columnNumber | |
| 1084 */ | |
| 1085 WebInspector.CSSLocation = function(header, lineNumber, columnNumber) | |
| 1086 { | |
| 1087 WebInspector.SDKObject.call(this, header.target()); | |
| 1088 this._header = header; | 1043 this._header = header; |
| 1089 this.styleSheetId = header.id; | 1044 this.styleSheetId = header.id; |
| 1090 this.url = header.resourceURL(); | 1045 this.url = header.resourceURL(); |
| 1091 this.lineNumber = lineNumber; | 1046 this.lineNumber = lineNumber; |
| 1092 this.columnNumber = columnNumber || 0; | 1047 this.columnNumber = columnNumber || 0; |
| 1048 } |
| 1049 |
| 1050 /** |
| 1051 * @return {!WebInspector.CSSModel} |
| 1052 */ |
| 1053 cssModel() { |
| 1054 return this._header.cssModel(); |
| 1055 } |
| 1056 |
| 1057 /** |
| 1058 * @return {!WebInspector.CSSStyleSheetHeader} |
| 1059 */ |
| 1060 header() { |
| 1061 return this._header; |
| 1062 } |
| 1093 }; | 1063 }; |
| 1094 | 1064 |
| 1095 WebInspector.CSSLocation.prototype = { | 1065 /** |
| 1096 /** | 1066 * @implements {CSSAgent.Dispatcher} |
| 1097 * @return {!WebInspector.CSSModel} | 1067 * @unrestricted |
| 1098 */ | 1068 */ |
| 1099 cssModel: function() | 1069 WebInspector.CSSDispatcher = class { |
| 1100 { | 1070 /** |
| 1101 return this._header.cssModel(); | 1071 * @param {!WebInspector.CSSModel} cssModel |
| 1102 }, | 1072 */ |
| 1103 | 1073 constructor(cssModel) { |
| 1104 /** | 1074 this._cssModel = cssModel; |
| 1105 * @return {!WebInspector.CSSStyleSheetHeader} | 1075 } |
| 1106 */ | 1076 |
| 1107 header: function() | 1077 /** |
| 1108 { | 1078 * @override |
| 1109 return this._header; | 1079 */ |
| 1110 }, | 1080 mediaQueryResultChanged() { |
| 1111 | 1081 this._cssModel.mediaQueryResultChanged(); |
| 1112 __proto__: WebInspector.SDKObject.prototype | 1082 } |
| 1083 |
| 1084 /** |
| 1085 * @override |
| 1086 */ |
| 1087 fontsUpdated() { |
| 1088 this._cssModel.fontsUpdated(); |
| 1089 } |
| 1090 |
| 1091 /** |
| 1092 * @override |
| 1093 * @param {!CSSAgent.StyleSheetId} styleSheetId |
| 1094 */ |
| 1095 styleSheetChanged(styleSheetId) { |
| 1096 this._cssModel._fireStyleSheetChanged(styleSheetId); |
| 1097 } |
| 1098 |
| 1099 /** |
| 1100 * @override |
| 1101 * @param {!CSSAgent.CSSStyleSheetHeader} header |
| 1102 */ |
| 1103 styleSheetAdded(header) { |
| 1104 this._cssModel._styleSheetAdded(header); |
| 1105 } |
| 1106 |
| 1107 /** |
| 1108 * @override |
| 1109 * @param {!CSSAgent.StyleSheetId} id |
| 1110 */ |
| 1111 styleSheetRemoved(id) { |
| 1112 this._cssModel._styleSheetRemoved(id); |
| 1113 } |
| 1114 |
| 1115 /** |
| 1116 * @override |
| 1117 * @param {!CSSAgent.StyleSheetId} id |
| 1118 * @param {!CSSAgent.SourceRange} range |
| 1119 */ |
| 1120 layoutEditorChange(id, range) { |
| 1121 this._cssModel._layoutEditorChange(id, range); |
| 1122 } |
| 1113 }; | 1123 }; |
| 1114 | 1124 |
| 1115 /** | 1125 /** |
| 1116 * @constructor | 1126 * @unrestricted |
| 1117 * @implements {CSSAgent.Dispatcher} | |
| 1118 * @param {!WebInspector.CSSModel} cssModel | |
| 1119 */ | 1127 */ |
| 1120 WebInspector.CSSDispatcher = function(cssModel) | 1128 WebInspector.CSSModel.ComputedStyleLoader = class { |
| 1121 { | 1129 /** |
| 1122 this._cssModel = cssModel; | 1130 * @param {!WebInspector.CSSModel} cssModel |
| 1123 }; | 1131 */ |
| 1124 | 1132 constructor(cssModel) { |
| 1125 WebInspector.CSSDispatcher.prototype = { | |
| 1126 /** | |
| 1127 * @override | |
| 1128 */ | |
| 1129 mediaQueryResultChanged: function() | |
| 1130 { | |
| 1131 this._cssModel.mediaQueryResultChanged(); | |
| 1132 }, | |
| 1133 | |
| 1134 /** | |
| 1135 * @override | |
| 1136 */ | |
| 1137 fontsUpdated: function() | |
| 1138 { | |
| 1139 this._cssModel.fontsUpdated(); | |
| 1140 }, | |
| 1141 | |
| 1142 /** | |
| 1143 * @override | |
| 1144 * @param {!CSSAgent.StyleSheetId} styleSheetId | |
| 1145 */ | |
| 1146 styleSheetChanged: function(styleSheetId) | |
| 1147 { | |
| 1148 this._cssModel._fireStyleSheetChanged(styleSheetId); | |
| 1149 }, | |
| 1150 | |
| 1151 /** | |
| 1152 * @override | |
| 1153 * @param {!CSSAgent.CSSStyleSheetHeader} header | |
| 1154 */ | |
| 1155 styleSheetAdded: function(header) | |
| 1156 { | |
| 1157 this._cssModel._styleSheetAdded(header); | |
| 1158 }, | |
| 1159 | |
| 1160 /** | |
| 1161 * @override | |
| 1162 * @param {!CSSAgent.StyleSheetId} id | |
| 1163 */ | |
| 1164 styleSheetRemoved: function(id) | |
| 1165 { | |
| 1166 this._cssModel._styleSheetRemoved(id); | |
| 1167 }, | |
| 1168 | |
| 1169 /** | |
| 1170 * @override | |
| 1171 * @param {!CSSAgent.StyleSheetId} id | |
| 1172 * @param {!CSSAgent.SourceRange} range | |
| 1173 */ | |
| 1174 layoutEditorChange: function(id, range) | |
| 1175 { | |
| 1176 this._cssModel._layoutEditorChange(id, range); | |
| 1177 }, | |
| 1178 }; | |
| 1179 | |
| 1180 /** | |
| 1181 * @constructor | |
| 1182 * @param {!WebInspector.CSSModel} cssModel | |
| 1183 */ | |
| 1184 WebInspector.CSSModel.ComputedStyleLoader = function(cssModel) | |
| 1185 { | |
| 1186 this._cssModel = cssModel; | 1133 this._cssModel = cssModel; |
| 1187 /** @type {!Map<!DOMAgent.NodeId, !Promise<?Map<string, string>>>} */ | 1134 /** @type {!Map<!DOMAgent.NodeId, !Promise<?Map<string, string>>>} */ |
| 1188 this._nodeIdToPromise = new Map(); | 1135 this._nodeIdToPromise = new Map(); |
| 1136 } |
| 1137 |
| 1138 /** |
| 1139 * @param {!DOMAgent.NodeId} nodeId |
| 1140 * @return {!Promise<?Map<string, string>>} |
| 1141 */ |
| 1142 computedStylePromise(nodeId) { |
| 1143 if (!this._nodeIdToPromise.has(nodeId)) |
| 1144 this._nodeIdToPromise.set( |
| 1145 nodeId, this._cssModel._agent.getComputedStyleForNode(nodeId, parsePay
load).then(cleanUp.bind(this))); |
| 1146 |
| 1147 return /** @type {!Promise.<?Map.<string, string>>} */ (this._nodeIdToPromis
e.get(nodeId)); |
| 1148 |
| 1149 /** |
| 1150 * @param {?Protocol.Error} error |
| 1151 * @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} computedPayload |
| 1152 * @return {?Map.<string, string>} |
| 1153 */ |
| 1154 function parsePayload(error, computedPayload) { |
| 1155 if (error || !computedPayload || !computedPayload.length) |
| 1156 return null; |
| 1157 var result = new Map(); |
| 1158 for (var property of computedPayload) |
| 1159 result.set(property.name, property.value); |
| 1160 return result; |
| 1161 } |
| 1162 |
| 1163 /** |
| 1164 * @param {?Map.<string, string>} computedStyle |
| 1165 * @return {?Map.<string, string>} |
| 1166 * @this {WebInspector.CSSModel.ComputedStyleLoader} |
| 1167 */ |
| 1168 function cleanUp(computedStyle) { |
| 1169 this._nodeIdToPromise.delete(nodeId); |
| 1170 return computedStyle; |
| 1171 } |
| 1172 } |
| 1189 }; | 1173 }; |
| 1190 | 1174 |
| 1191 WebInspector.CSSModel.ComputedStyleLoader.prototype = { | |
| 1192 /** | |
| 1193 * @param {!DOMAgent.NodeId} nodeId | |
| 1194 * @return {!Promise<?Map<string, string>>} | |
| 1195 */ | |
| 1196 computedStylePromise: function(nodeId) | |
| 1197 { | |
| 1198 if (!this._nodeIdToPromise.has(nodeId)) | |
| 1199 this._nodeIdToPromise.set(nodeId, this._cssModel._agent.getComputedS
tyleForNode(nodeId, parsePayload).then(cleanUp.bind(this))); | |
| 1200 | |
| 1201 return /** @type {!Promise.<?Map.<string, string>>} */(this._nodeIdToPro
mise.get(nodeId)); | |
| 1202 | |
| 1203 /** | |
| 1204 * @param {?Protocol.Error} error | |
| 1205 * @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} computedPayload | |
| 1206 * @return {?Map.<string, string>} | |
| 1207 */ | |
| 1208 function parsePayload(error, computedPayload) | |
| 1209 { | |
| 1210 if (error || !computedPayload || !computedPayload.length) | |
| 1211 return null; | |
| 1212 var result = new Map(); | |
| 1213 for (var property of computedPayload) | |
| 1214 result.set(property.name, property.value); | |
| 1215 return result; | |
| 1216 } | |
| 1217 | |
| 1218 /** | |
| 1219 * @param {?Map.<string, string>} computedStyle | |
| 1220 * @return {?Map.<string, string>} | |
| 1221 * @this {WebInspector.CSSModel.ComputedStyleLoader} | |
| 1222 */ | |
| 1223 function cleanUp(computedStyle) | |
| 1224 { | |
| 1225 this._nodeIdToPromise.delete(nodeId); | |
| 1226 return computedStyle; | |
| 1227 } | |
| 1228 } | |
| 1229 }; | |
| 1230 | 1175 |
| 1231 /** | 1176 /** |
| 1232 * @param {!WebInspector.Target} target | 1177 * @unrestricted |
| 1233 * @return {?WebInspector.CSSModel} | |
| 1234 */ | 1178 */ |
| 1235 WebInspector.CSSModel.fromTarget = function(target) | 1179 WebInspector.CSSModel.InlineStyleResult = class { |
| 1236 { | 1180 /** |
| 1237 return /** @type {?WebInspector.CSSModel} */ (target.model(WebInspector.CSSM
odel)); | 1181 * @param {?WebInspector.CSSStyleDeclaration} inlineStyle |
| 1238 }; | 1182 * @param {?WebInspector.CSSStyleDeclaration} attributesStyle |
| 1239 | 1183 */ |
| 1240 /** | 1184 constructor(inlineStyle, attributesStyle) { |
| 1241 * @param {!WebInspector.DOMNode} node | |
| 1242 * @return {!WebInspector.CSSModel} | |
| 1243 */ | |
| 1244 WebInspector.CSSModel.fromNode = function(node) | |
| 1245 { | |
| 1246 return /** @type {!WebInspector.CSSModel} */ (WebInspector.CSSModel.fromTarg
et(node.target())); | |
| 1247 }; | |
| 1248 | |
| 1249 /** | |
| 1250 * @constructor | |
| 1251 * @param {?WebInspector.CSSStyleDeclaration} inlineStyle | |
| 1252 * @param {?WebInspector.CSSStyleDeclaration} attributesStyle | |
| 1253 */ | |
| 1254 WebInspector.CSSModel.InlineStyleResult = function(inlineStyle, attributesStyle) | |
| 1255 { | |
| 1256 this.inlineStyle = inlineStyle; | 1185 this.inlineStyle = inlineStyle; |
| 1257 this.attributesStyle = attributesStyle; | 1186 this.attributesStyle = attributesStyle; |
| 1187 } |
| 1258 }; | 1188 }; |
| OLD | NEW |