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 |