Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/CSSModel.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698