| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @implements {SDK.SDKModelObserver<!SDK.CSSModel>} |
| */ |
| export default class CSSWorkspaceBinding { |
| /** |
| * @param {!SDK.TargetManager} targetManager |
| * @param {!Workspace.Workspace} workspace |
| */ |
| constructor(targetManager, workspace) { |
| this._workspace = workspace; |
| |
| /** @type {!Map.<!SDK.CSSModel, !ModelInfo>} */ |
| this._modelToInfo = new Map(); |
| /** @type {!Array<!SourceMapping>} */ |
| this._sourceMappings = []; |
| targetManager.observeModels(SDK.CSSModel, this); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.CSSModel} cssModel |
| */ |
| modelAdded(cssModel) { |
| this._modelToInfo.set(cssModel, new ModelInfo(cssModel, this._workspace)); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.CSSModel} cssModel |
| */ |
| modelRemoved(cssModel) { |
| this._modelToInfo.get(cssModel)._dispose(); |
| this._modelToInfo.delete(cssModel); |
| } |
| |
| /** |
| * @param {!SDK.CSSStyleSheetHeader} header |
| */ |
| updateLocations(header) { |
| this._modelToInfo.get(header.cssModel())._updateLocations(header); |
| } |
| |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @param {function(!Bindings.LiveLocation)} updateDelegate |
| * @param {!Bindings.LiveLocationPool} locationPool |
| * @return {!LiveLocation} |
| */ |
| createLiveLocation(rawLocation, updateDelegate, locationPool) { |
| return this._modelToInfo.get(rawLocation.cssModel())._createLiveLocation(rawLocation, updateDelegate, locationPool); |
| } |
| |
| /** |
| * @param {!SDK.CSSProperty} cssProperty |
| * @param {boolean} forName |
| * @return {?Workspace.UILocation} |
| */ |
| propertyUILocation(cssProperty, forName) { |
| const style = cssProperty.ownerStyle; |
| if (!style || style.type !== SDK.CSSStyleDeclaration.Type.Regular || !style.styleSheetId) { |
| return null; |
| } |
| const header = style.cssModel().styleSheetHeaderForId(style.styleSheetId); |
| if (!header) { |
| return null; |
| } |
| |
| const range = forName ? cssProperty.nameRange() : cssProperty.valueRange(); |
| if (!range) { |
| return null; |
| } |
| |
| const lineNumber = range.startLine; |
| const columnNumber = range.startColumn; |
| const rawLocation = new SDK.CSSLocation( |
| header, header.lineNumberInSource(lineNumber), header.columnNumberInSource(lineNumber, columnNumber)); |
| return this.rawLocationToUILocation(rawLocation); |
| } |
| |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @return {?Workspace.UILocation} |
| */ |
| rawLocationToUILocation(rawLocation) { |
| for (let i = this._sourceMappings.length - 1; i >= 0; --i) { |
| const uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation); |
| if (uiLocation) { |
| return uiLocation; |
| } |
| } |
| return this._modelToInfo.get(rawLocation.cssModel())._rawLocationToUILocation(rawLocation); |
| } |
| |
| /** |
| * @param {!Workspace.UILocation} uiLocation |
| * @return {!Array<!SDK.CSSLocation>} |
| */ |
| uiLocationToRawLocations(uiLocation) { |
| for (let i = this._sourceMappings.length - 1; i >= 0; --i) { |
| const rawLocations = this._sourceMappings[i].uiLocationToRawLocations(uiLocation); |
| if (rawLocations.length) { |
| return rawLocations; |
| } |
| } |
| const rawLocations = []; |
| for (const modelInfo of this._modelToInfo.values()) { |
| rawLocations.pushAll(modelInfo._uiLocationToRawLocations(uiLocation)); |
| } |
| return rawLocations; |
| } |
| |
| /** |
| * @param {!SourceMapping} sourceMapping |
| */ |
| addSourceMapping(sourceMapping) { |
| this._sourceMappings.push(sourceMapping); |
| } |
| } |
| |
| /** |
| * @interface |
| */ |
| class SourceMapping { |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @return {?Workspace.UILocation} |
| */ |
| rawLocationToUILocation(rawLocation) { |
| } |
| |
| /** |
| * @param {!Workspace.UILocation} uiLocation |
| * @return {!Array<!SDK.CSSLocation>} |
| */ |
| uiLocationToRawLocations(uiLocation) { |
| } |
| } |
| |
| class ModelInfo { |
| /** |
| * @param {!SDK.CSSModel} cssModel |
| * @param {!Workspace.Workspace} workspace |
| */ |
| constructor(cssModel, workspace) { |
| this._eventListeners = [ |
| cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this._styleSheetAdded, this), |
| cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this) |
| ]; |
| |
| this._stylesSourceMapping = new Bindings.StylesSourceMapping(cssModel, workspace); |
| const sourceMapManager = cssModel.sourceMapManager(); |
| this._sassSourceMapping = new Bindings.SASSSourceMapping(cssModel.target(), sourceMapManager, workspace); |
| |
| /** @type {!Platform.Multimap<!SDK.CSSStyleSheetHeader, !LiveLocation>} */ |
| this._locations = new Platform.Multimap(); |
| /** @type {!Platform.Multimap<string, !LiveLocation>} */ |
| this._unboundLocations = new Platform.Multimap(); |
| } |
| |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @param {function(!Bindings.LiveLocation)} updateDelegate |
| * @param {!Bindings.LiveLocationPool} locationPool |
| * @return {!LiveLocation} |
| */ |
| _createLiveLocation(rawLocation, updateDelegate, locationPool) { |
| const location = new LiveLocation(rawLocation, this, updateDelegate, locationPool); |
| const header = rawLocation.header(); |
| if (header) { |
| location._header = header; |
| this._locations.set(header, location); |
| location.update(); |
| } else { |
| this._unboundLocations.set(rawLocation.url, location); |
| } |
| return location; |
| } |
| |
| /** |
| * @param {!LiveLocation} location |
| */ |
| _disposeLocation(location) { |
| if (location._header) { |
| this._locations.delete(location._header, location); |
| } else { |
| this._unboundLocations.delete(location._url, location); |
| } |
| } |
| |
| /** |
| * @param {!SDK.CSSStyleSheetHeader} header |
| */ |
| _updateLocations(header) { |
| for (const location of this._locations.get(header)) { |
| location.update(); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _styleSheetAdded(event) { |
| const header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data); |
| if (!header.sourceURL) { |
| return; |
| } |
| |
| for (const location of this._unboundLocations.get(header.sourceURL)) { |
| location._header = header; |
| this._locations.set(header, location); |
| location.update(); |
| } |
| this._unboundLocations.deleteAll(header.sourceURL); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _styleSheetRemoved(event) { |
| const header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data); |
| for (const location of this._locations.get(header)) { |
| location._header = null; |
| this._unboundLocations.set(location._url, location); |
| location.update(); |
| } |
| this._locations.deleteAll(header); |
| } |
| |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @return {?Workspace.UILocation} |
| */ |
| _rawLocationToUILocation(rawLocation) { |
| let uiLocation = null; |
| uiLocation = uiLocation || this._sassSourceMapping.rawLocationToUILocation(rawLocation); |
| uiLocation = uiLocation || this._stylesSourceMapping.rawLocationToUILocation(rawLocation); |
| uiLocation = uiLocation || Bindings.resourceMapping.cssLocationToUILocation(rawLocation); |
| return uiLocation; |
| } |
| |
| /** |
| * @param {!Workspace.UILocation} uiLocation |
| * @return {!Array<!SDK.CSSLocation>} |
| */ |
| _uiLocationToRawLocations(uiLocation) { |
| let rawLocations = this._sassSourceMapping.uiLocationToRawLocations(uiLocation); |
| if (rawLocations.length) { |
| return rawLocations; |
| } |
| rawLocations = this._stylesSourceMapping.uiLocationToRawLocations(uiLocation); |
| if (rawLocations.length) { |
| return rawLocations; |
| } |
| return Bindings.resourceMapping.uiLocationToCSSLocations(uiLocation); |
| } |
| |
| _dispose() { |
| Common.EventTarget.removeEventListeners(this._eventListeners); |
| this._stylesSourceMapping.dispose(); |
| this._sassSourceMapping.dispose(); |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| class LiveLocation extends Bindings.LiveLocationWithPool { |
| /** |
| * @param {!SDK.CSSLocation} rawLocation |
| * @param {!ModelInfo} info |
| * @param {function(!Bindings.LiveLocation)} updateDelegate |
| * @param {!Bindings.LiveLocationPool} locationPool |
| */ |
| constructor(rawLocation, info, updateDelegate, locationPool) { |
| super(updateDelegate, locationPool); |
| this._url = rawLocation.url; |
| this._lineNumber = rawLocation.lineNumber; |
| this._columnNumber = rawLocation.columnNumber; |
| this._info = info; |
| this._header = null; |
| } |
| |
| /** |
| * @override |
| * @return {?Workspace.UILocation} |
| */ |
| uiLocation() { |
| if (!this._header) { |
| return null; |
| } |
| const rawLocation = new SDK.CSSLocation(this._header, this._lineNumber, this._columnNumber); |
| return Bindings.cssWorkspaceBinding.rawLocationToUILocation(rawLocation); |
| } |
| |
| /** |
| * @override |
| */ |
| dispose() { |
| super.dispose(); |
| this._info._disposeLocation(this); |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| isBlackboxed() { |
| return false; |
| } |
| } |
| |
| /* Legacy exported object */ |
| self.Bindings = self.Bindings || {}; |
| |
| /* Legacy exported object */ |
| Bindings = Bindings || {}; |
| |
| /** @constructor */ |
| Bindings.CSSWorkspaceBinding = CSSWorkspaceBinding; |
| |
| /** @interface */ |
| Bindings.CSSWorkspaceBinding.SourceMapping = SourceMapping; |
| |
| /** @constructor */ |
| Bindings.CSSWorkspaceBinding.ModelInfo = ModelInfo; |
| |
| /** |
| * @type {!CSSWorkspaceBinding} |
| */ |
| Bindings.cssWorkspaceBinding; |