1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @implements {WebInspector.TargetManager.Observer}
9 WebInspector.CSSWorkspaceBinding = function()
11 /** @type {!Map.<!WebInspector.Target, !WebInspector.CSSWorkspaceBinding.TargetInfo>} */
12 this._targetToTargetInfo = new Map();
13 WebInspector.targetManager.observeTargets(this);
15 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameCreatedOrNavigated, this);
18 WebInspector.CSSWorkspaceBinding.prototype = {
20 * @param {!WebInspector.Target} target
22 targetAdded: function(target)
24 this._targetToTargetInfo.put(target, new WebInspector.CSSWorkspaceBinding.TargetInfo(target, WebInspector.workspace, WebInspector.networkWorkspaceBinding));
28 * @param {!WebInspector.Target} target
30 targetRemoved: function(target)
32 this._targetToTargetInfo.remove(target)._dispose();
36 * @param {!WebInspector.CSSStyleSheetHeader} header
37 * @param {!WebInspector.CSSSourceMapping} mapping
39 pushSourceMapping: function(header, mapping)
41 this._ensureInfoForHeader(header)._pushSourceMapping(mapping);
45 * @param {!WebInspector.CSSStyleSheetHeader} header
46 * @return {?WebInspector.CSSWorkspaceBinding.HeaderInfo}
48 _headerInfo: function(header)
50 var map = this._targetToTargetInfo.get(header.target());
51 return map._headerInfo(header.id) || null;
55 * @param {!WebInspector.CSSStyleSheetHeader} header
56 * @return {!WebInspector.CSSWorkspaceBinding.HeaderInfo}
58 _ensureInfoForHeader: function(header)
60 var targetInfo = this._targetToTargetInfo.get(header.target());
62 targetInfo = new WebInspector.CSSWorkspaceBinding.TargetInfo(header.target(), WebInspector.workspace, WebInspector.networkWorkspaceBinding);
63 this._targetToTargetInfo.put(header.target(), targetInfo);
65 return targetInfo._ensureInfoForHeader(header);
69 * @param {!WebInspector.Event} event
71 _mainFrameCreatedOrNavigated: function(event)
73 var target = /** @type {!WebInspector.ResourceTreeModel} */ (event.target).target();
74 this._targetToTargetInfo.get(target)._reset();
78 * @param {!WebInspector.CSSStyleSheetHeader} header
80 updateLocations: function(header)
82 var info = this._headerInfo(header);
84 info._updateLocations();
88 * @param {!WebInspector.CSSLocation} rawLocation
89 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
90 * @return {!WebInspector.CSSWorkspaceBinding.LiveLocation}
92 createLiveLocation: function(rawLocation, updateDelegate)
94 var header = rawLocation.styleSheetId ? rawLocation.target().cssModel.styleSheetHeaderForId(rawLocation.styleSheetId) : null;
95 return new WebInspector.CSSWorkspaceBinding.LiveLocation(rawLocation.target().cssModel, header, rawLocation, updateDelegate);
99 * @param {!WebInspector.CSSWorkspaceBinding.LiveLocation} location
101 _addLiveLocation: function(location)
103 this._ensureInfoForHeader(location._header)._addLocation(location);
107 * @param {!WebInspector.CSSWorkspaceBinding.LiveLocation} location
109 _removeLiveLocation: function(location)
111 var info = this._headerInfo(location._header);
113 info._removeLocation(location);
117 * @param {!WebInspector.CSSProperty} cssProperty
118 * @param {boolean} forName
119 * @return {?WebInspector.UILocation}
121 propertyUILocation: function(cssProperty, forName)
123 var style = cssProperty.ownerStyle;
124 if (!style || !style.parentRule || !style.styleSheetId)
127 var range = cssProperty.range;
131 var url = style.parentRule.resourceURL();
135 var line = forName ? range.startLine : range.endLine;
136 // End of range is exclusive, so subtract 1 from the end offset.
137 var column = forName ? range.startColumn : range.endColumn - (cssProperty.text && cssProperty.text.endsWith(";") ? 2 : 1);
138 var rawLocation = new WebInspector.CSSLocation(style.target(), style.styleSheetId, url, line, column);
139 return this.rawLocationToUILocation(rawLocation);
143 * @param {?WebInspector.CSSLocation} rawLocation
144 * @return {?WebInspector.UILocation}
146 rawLocationToUILocation: function(rawLocation)
150 var cssModel = rawLocation.target().cssModel;
151 var frameIdToSheetIds = cssModel.styleSheetIdsByFrameIdForURL(rawLocation.url);
152 if (!Object.values(frameIdToSheetIds).length)
154 var styleSheetIds = [];
155 for (var frameId in frameIdToSheetIds)
156 styleSheetIds = styleSheetIds.concat(frameIdToSheetIds[frameId]);
158 for (var i = 0; !uiLocation && i < styleSheetIds.length; ++i) {
159 var header = cssModel.styleSheetHeaderForId(styleSheetIds[i]);
162 var info = this._headerInfo(header);
164 uiLocation = info._rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber);
166 return uiLocation || null;
172 * @param {!WebInspector.Target} target
173 * @param {!WebInspector.Workspace} workspace
174 * @param {!WebInspector.NetworkWorkspaceBinding} networkWorkspaceBinding
176 WebInspector.CSSWorkspaceBinding.TargetInfo = function(target, workspace, networkWorkspaceBinding)
178 this._target = target;
179 this._workspace = workspace;
181 var cssModel = target.cssModel;
182 this._stylesSourceMapping = new WebInspector.StylesSourceMapping(cssModel, workspace);
183 this._sassSourceMapping = new WebInspector.SASSSourceMapping(cssModel, workspace, networkWorkspaceBinding);
185 /** @type {!StringMap.<!WebInspector.CSSWorkspaceBinding.HeaderInfo>} */
186 this._headerInfoById = new StringMap();
188 cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
189 cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
192 WebInspector.CSSWorkspaceBinding.TargetInfo.prototype = {
194 * @param {!WebInspector.Event} event
196 _styleSheetAdded: function(event)
198 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
199 this._stylesSourceMapping.addHeader(header);
200 this._sassSourceMapping.addHeader(header);
204 * @param {!WebInspector.Event} event
206 _styleSheetRemoved: function(event)
208 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
209 this._stylesSourceMapping.removeHeader(header);
210 this._sassSourceMapping.removeHeader(header);
211 this._headerInfoById.remove(header.id);
215 * @param {!CSSAgent.StyleSheetId} id
217 _headerInfo: function(id)
219 return this._headerInfoById.get(id);
222 _ensureInfoForHeader: function(header)
224 var info = this._headerInfoById.get(header.id);
226 info = new WebInspector.CSSWorkspaceBinding.HeaderInfo(header);
227 this._headerInfoById.put(header.id, info);
235 this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
236 this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
241 this._headerInfoById.clear();
247 * @param {!WebInspector.CSSStyleSheetHeader} header
249 WebInspector.CSSWorkspaceBinding.HeaderInfo = function(header)
251 this._header = header;
253 /** @type {!Array.<!WebInspector.CSSSourceMapping>} */
254 this._sourceMappings = [];
256 /** @type {!Set.<!WebInspector.LiveLocation>} */
257 this._locations = new Set();
260 WebInspector.CSSWorkspaceBinding.HeaderInfo.prototype = {
262 * @param {!WebInspector.LiveLocation} location
264 _addLocation: function(location)
266 this._locations.add(location);
271 * @param {!WebInspector.LiveLocation} location
273 _removeLocation: function(location)
275 this._locations.remove(location);
278 _updateLocations: function()
280 var items = this._locations.values();
281 for (var i = 0; i < items.length; ++i)
286 * @param {number} lineNumber
287 * @param {number=} columnNumber
288 * @return {?WebInspector.UILocation}
290 _rawLocationToUILocation: function(lineNumber, columnNumber)
292 var uiLocation = null;
293 var rawLocation = new WebInspector.CSSLocation(this._header.target(), this._header.id, this._header.resourceURL(), lineNumber, columnNumber);
294 for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
295 uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
300 * @param {!WebInspector.CSSSourceMapping} sourceMapping
302 _pushSourceMapping: function(sourceMapping)
304 this._sourceMappings.push(sourceMapping);
305 this._updateLocations();
311 * @extends {WebInspector.LiveLocation}
312 * @param {!WebInspector.CSSStyleModel} cssModel
313 * @param {?WebInspector.CSSStyleSheetHeader} header
314 * @param {!WebInspector.CSSLocation} rawLocation
315 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
317 WebInspector.CSSWorkspaceBinding.LiveLocation = function(cssModel, header, rawLocation, updateDelegate)
319 WebInspector.LiveLocation.call(this, updateDelegate);
320 this._cssModel = cssModel;
321 this._rawLocation = rawLocation;
323 this._clearStyleSheet();
325 this._setStyleSheet(header);
328 WebInspector.CSSWorkspaceBinding.LiveLocation.prototype = {
330 * @param {!WebInspector.Event} event
332 _styleSheetAdded: function(event)
334 console.assert(!this._header);
335 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
336 if (header.sourceURL && header.sourceURL === this._rawLocation.url)
337 this._setStyleSheet(header);
341 * @param {!WebInspector.Event} event
343 _styleSheetRemoved: function(event)
345 console.assert(this._header);
346 var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
347 if (this._header !== header)
349 WebInspector.cssWorkspaceBinding._removeLiveLocation(this);
350 this._clearStyleSheet();
354 * @param {!WebInspector.CSSStyleSheetHeader} header
356 _setStyleSheet: function(header)
358 this._header = header;
359 WebInspector.cssWorkspaceBinding._addLiveLocation(this);
360 this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
361 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
364 _clearStyleSheet: function()
367 this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
368 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
372 * @return {?WebInspector.UILocation}
374 uiLocation: function()
376 var cssLocation = this._rawLocation;
378 var headerInfo = WebInspector.cssWorkspaceBinding._headerInfo(this._header);
379 return headerInfo._rawLocationToUILocation(cssLocation.lineNumber, cssLocation.columnNumber);
381 var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(cssLocation.url);
384 return uiSourceCode.uiLocation(cssLocation.lineNumber, cssLocation.columnNumber);
389 WebInspector.LiveLocation.prototype.dispose.call(this);
391 WebInspector.cssWorkspaceBinding._removeLiveLocation(this);
392 this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
393 this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
396 __proto__: WebInspector.LiveLocation.prototype
402 WebInspector.CSSSourceMapping = function()
406 WebInspector.CSSSourceMapping.prototype = {
408 * @param {!WebInspector.CSSLocation} rawLocation
409 * @return {?WebInspector.UILocation}
411 rawLocationToUILocation: function(rawLocation) { },
414 * @param {!WebInspector.UISourceCode} uiSourceCode
415 * @param {number} lineNumber
416 * @param {number} columnNumber
417 * @return {?WebInspector.CSSLocation}
419 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) { },
424 isIdentity: function() { },
427 * @param {!WebInspector.UISourceCode} uiSourceCode
428 * @param {number} lineNumber
431 uiLineHasMapping: function(uiSourceCode, lineNumber) { }
435 * @type {!WebInspector.CSSWorkspaceBinding}
437 WebInspector.cssWorkspaceBinding;