| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| * @param {!WebInspector.Workspace} workspace |
| */ |
| WebInspector.CSSStyleModel = function(workspace) |
| { |
| this._workspace = workspace; |
| this._pendingCommandsMajorState = []; |
| this._styleLoader = new WebInspector.CSSStyleModel.ComputedStyleLoader(this); |
| WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoRequested, this._undoRedoRequested, this); |
| WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoCompleted, this._undoRedoCompleted, this); |
| WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this); |
| this._namedFlowCollections = {}; |
| WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._resetNamedFlowCollections, this); |
| InspectorBackend.registerCSSDispatcher(new WebInspector.CSSDispatcher(this)); |
| CSSAgent.enable(this._wasEnabled.bind(this)); |
| this._resetStyleSheets(); |
| } |
| |
| /** |
| * @param {!Array.<!CSSAgent.RuleMatch>|undefined} matchArray |
| */ |
| WebInspector.CSSStyleModel.parseRuleMatchArrayPayload = function(matchArray) |
| { |
| if (!matchArray) |
| return []; |
| |
| var result = []; |
| for (var i = 0; i < matchArray.length; ++i) |
| result.push(WebInspector.CSSRule.parsePayload(matchArray[i].rule, matchArray[i].matchingSelectors)); |
| return result; |
| } |
| |
| WebInspector.CSSStyleModel.Events = { |
| ModelWasEnabled: "ModelWasEnabled", |
| StyleSheetAdded: "StyleSheetAdded", |
| StyleSheetChanged: "StyleSheetChanged", |
| StyleSheetRemoved: "StyleSheetRemoved", |
| MediaQueryResultChanged: "MediaQueryResultChanged", |
| NamedFlowCreated: "NamedFlowCreated", |
| NamedFlowRemoved: "NamedFlowRemoved", |
| RegionLayoutUpdated: "RegionLayoutUpdated", |
| RegionOversetChanged: "RegionOversetChanged" |
| } |
| |
| WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"]; |
| |
| WebInspector.CSSStyleModel.prototype = { |
| /** |
| * @return {boolean} |
| */ |
| isEnabled: function() |
| { |
| return this._isEnabled; |
| }, |
| |
| _wasEnabled: function() |
| { |
| this._isEnabled = true; |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.ModelWasEnabled); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {boolean} needPseudo |
| * @param {boolean} needInherited |
| * @param {function(?*)} userCallback |
| */ |
| getMatchedStylesAsync: function(nodeId, needPseudo, needInherited, userCallback) |
| { |
| /** |
| * @param {function(?*)} userCallback |
| * @param {?Protocol.Error} error |
| * @param {!Array.<!CSSAgent.RuleMatch>=} matchedPayload |
| * @param {!Array.<!CSSAgent.PseudoIdMatches>=} pseudoPayload |
| * @param {!Array.<!CSSAgent.InheritedStyleEntry>=} inheritedPayload |
| */ |
| function callback(userCallback, error, matchedPayload, pseudoPayload, inheritedPayload) |
| { |
| if (error) { |
| if (userCallback) |
| userCallback(null); |
| return; |
| } |
| |
| var result = {}; |
| result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(matchedPayload); |
| |
| result.pseudoElements = []; |
| if (pseudoPayload) { |
| for (var i = 0; i < pseudoPayload.length; ++i) { |
| var entryPayload = pseudoPayload[i]; |
| result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matches) }); |
| } |
| } |
| |
| result.inherited = []; |
| if (inheritedPayload) { |
| for (var i = 0; i < inheritedPayload.length; ++i) { |
| var entryPayload = inheritedPayload[i]; |
| var entry = {}; |
| if (entryPayload.inlineStyle) |
| entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle); |
| if (entryPayload.matchedCSSRules) |
| entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matchedCSSRules); |
| result.inherited.push(entry); |
| } |
| } |
| |
| if (userCallback) |
| userCallback(result); |
| } |
| |
| CSSAgent.getMatchedStylesForNode(nodeId, needPseudo, needInherited, callback.bind(null, userCallback)); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback |
| */ |
| getComputedStyleAsync: function(nodeId, userCallback) |
| { |
| this._styleLoader.getComputedStyle(nodeId, userCallback); |
| }, |
| |
| /** |
| * @param {number} nodeId |
| * @param {function(?String, ?Array.<!CSSAgent.PlatformFontUsage>)} callback |
| */ |
| getPlatformFontsForNode: function(nodeId, callback) |
| { |
| function platformFontsCallback(error, cssFamilyName, fonts) |
| { |
| if (error) |
| callback(null, null); |
| else |
| callback(cssFamilyName, fonts); |
| } |
| CSSAgent.getPlatformFontsForNode(nodeId, platformFontsCallback); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback |
| */ |
| getInlineStylesAsync: function(nodeId, userCallback) |
| { |
| /** |
| * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback |
| * @param {?Protocol.Error} error |
| * @param {?CSSAgent.CSSStyle=} inlinePayload |
| * @param {?CSSAgent.CSSStyle=} attributesStylePayload |
| */ |
| function callback(userCallback, error, inlinePayload, attributesStylePayload) |
| { |
| if (error || !inlinePayload) |
| userCallback(null, null); |
| else |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(inlinePayload), attributesStylePayload ? WebInspector.CSSStyleDeclaration.parsePayload(attributesStylePayload) : null); |
| } |
| |
| CSSAgent.getInlineStylesForNode(nodeId, callback.bind(null, userCallback)); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {?Array.<string>|undefined} forcedPseudoClasses |
| * @param {function()=} userCallback |
| */ |
| forcePseudoState: function(nodeId, forcedPseudoClasses, userCallback) |
| { |
| CSSAgent.forcePseudoState(nodeId, forcedPseudoClasses || [], userCallback); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} documentNodeId |
| * @param {function(?WebInspector.NamedFlowCollection)} userCallback |
| */ |
| getNamedFlowCollectionAsync: function(documentNodeId, userCallback) |
| { |
| var namedFlowCollection = this._namedFlowCollections[documentNodeId]; |
| if (namedFlowCollection) { |
| userCallback(namedFlowCollection); |
| return; |
| } |
| |
| /** |
| * @param {function(?WebInspector.NamedFlowCollection)} userCallback |
| * @param {?Protocol.Error} error |
| * @param {?Array.<!CSSAgent.NamedFlow>} namedFlowPayload |
| */ |
| function callback(userCallback, error, namedFlowPayload) |
| { |
| if (error || !namedFlowPayload) |
| userCallback(null); |
| else { |
| var namedFlowCollection = new WebInspector.NamedFlowCollection(namedFlowPayload); |
| this._namedFlowCollections[documentNodeId] = namedFlowCollection; |
| userCallback(namedFlowCollection); |
| } |
| } |
| |
| CSSAgent.getNamedFlowCollection(documentNodeId, callback.bind(this, userCallback)); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} documentNodeId |
| * @param {string} flowName |
| * @param {function(?WebInspector.NamedFlow)} userCallback |
| */ |
| getFlowByNameAsync: function(documentNodeId, flowName, userCallback) |
| { |
| var namedFlowCollection = this._namedFlowCollections[documentNodeId]; |
| if (namedFlowCollection) { |
| userCallback(namedFlowCollection.flowByName(flowName)); |
| return; |
| } |
| |
| /** |
| * @param {function(?WebInspector.NamedFlow)} userCallback |
| * @param {?WebInspector.NamedFlowCollection} namedFlowCollection |
| */ |
| function callback(userCallback, namedFlowCollection) |
| { |
| if (!namedFlowCollection) |
| userCallback(null); |
| else |
| userCallback(namedFlowCollection.flowByName(flowName)); |
| } |
| |
| this.getNamedFlowCollectionAsync(documentNodeId, callback.bind(this, userCallback)); |
| }, |
| |
| /** |
| * @param {!CSSAgent.CSSRuleId} ruleId |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {string} newSelector |
| * @param {function(!WebInspector.CSSRule)} successCallback |
| * @param {function()} failureCallback |
| */ |
| setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback) |
| { |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {function(!WebInspector.CSSRule)} successCallback |
| * @param {function()} failureCallback |
| * @param {?Protocol.Error} error |
| * @param {string} newSelector |
| * @param {?CSSAgent.CSSRule} rulePayload |
| */ |
| function callback(nodeId, successCallback, failureCallback, newSelector, error, rulePayload) |
| { |
| this._pendingCommandsMajorState.pop(); |
| if (error) { |
| failureCallback(); |
| return; |
| } |
| WebInspector.domAgent.markUndoableState(); |
| this._computeMatchingSelectors(rulePayload, nodeId, successCallback, failureCallback); |
| } |
| |
| |
| this._pendingCommandsMajorState.push(true); |
| CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback, newSelector)); |
| }, |
| |
| /** |
| * @param {!CSSAgent.CSSRule} rulePayload |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {function(!WebInspector.CSSRule)} successCallback |
| * @param {function()} failureCallback |
| */ |
| _computeMatchingSelectors: function(rulePayload, nodeId, successCallback, failureCallback) |
| { |
| var ownerDocumentId = this._ownerDocumentId(nodeId); |
| if (!ownerDocumentId) { |
| failureCallback(); |
| return; |
| } |
| var rule = WebInspector.CSSRule.parsePayload(rulePayload); |
| var matchingSelectors = []; |
| var allSelectorsBarrier = new CallbackBarrier(); |
| for (var i = 0; i < rule.selectors.length; ++i) { |
| var selector = rule.selectors[i]; |
| var boundCallback = allSelectorsBarrier.createCallback(selectorQueried.bind(this, i, nodeId, matchingSelectors)); |
| WebInspector.domAgent.querySelectorAll(ownerDocumentId, selector.value, boundCallback); |
| } |
| allSelectorsBarrier.callWhenDone(function() { |
| rule.matchingSelectors = matchingSelectors; |
| successCallback(rule); |
| }); |
| |
| /** |
| * @param {number} index |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {!Array.<number>} matchingSelectors |
| * @param {!Array.<!DOMAgent.NodeId>=} matchingNodeIds |
| */ |
| function selectorQueried(index, nodeId, matchingSelectors, matchingNodeIds) |
| { |
| if (!matchingNodeIds) |
| return; |
| if (matchingNodeIds.indexOf(nodeId) !== -1) |
| matchingSelectors.push(index); |
| } |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {string} selector |
| * @param {function(!WebInspector.CSSRule)} successCallback |
| * @param {function()} failureCallback |
| */ |
| addRule: function(nodeId, selector, successCallback, failureCallback) |
| { |
| /** |
| * @param {function(!WebInspector.CSSRule, boolean)} successCallback |
| * @param {function()} failureCallback |
| * @param {string} selector |
| * @param {?Protocol.Error} error |
| * @param {?CSSAgent.CSSRule} rulePayload |
| */ |
| function callback(successCallback, failureCallback, selector, error, rulePayload) |
| { |
| this._pendingCommandsMajorState.pop(); |
| if (error) { |
| // Invalid syntax for a selector |
| failureCallback(); |
| } else { |
| WebInspector.domAgent.markUndoableState(); |
| this._computeMatchingSelectors(rulePayload, nodeId, successCallback, failureCallback); |
| } |
| } |
| |
| this._pendingCommandsMajorState.push(true); |
| CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector)); |
| }, |
| |
| mediaQueryResultChanged: function() |
| { |
| this._styleLoader.reset(); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged); |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} id |
| * @return {!WebInspector.CSSStyleSheetHeader} |
| */ |
| styleSheetHeaderForId: function(id) |
| { |
| return this._styleSheetIdToHeader[id]; |
| }, |
| |
| /** |
| * @return {!Array.<!WebInspector.CSSStyleSheetHeader>} |
| */ |
| styleSheetHeaders: function() |
| { |
| return Object.values(this._styleSheetIdToHeader); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @return {?DOMAgent.NodeId} |
| */ |
| _ownerDocumentId: function(nodeId) |
| { |
| var node = WebInspector.domAgent.nodeForId(nodeId); |
| if (!node) |
| return null; |
| return node.ownerDocument ? node.ownerDocument.id : null; |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} styleSheetId |
| */ |
| _fireStyleSheetChanged: function(styleSheetId) |
| { |
| this._styleLoader.reset(); |
| if (!this._pendingCommandsMajorState.length) |
| return; |
| |
| var majorChange = this._pendingCommandsMajorState[this._pendingCommandsMajorState.length - 1]; |
| |
| if (!styleSheetId || !this.hasEventListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged)) |
| return; |
| |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, majorChange: majorChange }); |
| }, |
| |
| /** |
| * @param {!CSSAgent.CSSStyleSheetHeader} header |
| */ |
| _styleSheetAdded: function(header) |
| { |
| console.assert(!this._styleSheetIdToHeader[header.styleSheetId]); |
| var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(header); |
| this._styleSheetIdToHeader[header.styleSheetId] = styleSheetHeader; |
| var url = styleSheetHeader.resourceURL(); |
| if (!this._styleSheetIdsForURL[url]) |
| this._styleSheetIdsForURL[url] = {}; |
| var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url]; |
| var styleSheetIds = frameIdToStyleSheetIds[styleSheetHeader.frameId]; |
| if (!styleSheetIds) { |
| styleSheetIds = []; |
| frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds; |
| } |
| styleSheetIds.push(styleSheetHeader.id); |
| this._styleLoader.reset(); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetAdded, styleSheetHeader); |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} id |
| */ |
| _styleSheetRemoved: function(id) |
| { |
| var header = this._styleSheetIdToHeader[id]; |
| console.assert(header); |
| delete this._styleSheetIdToHeader[id]; |
| var url = header.resourceURL(); |
| var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url]; |
| frameIdToStyleSheetIds[header.frameId].remove(id); |
| if (!frameIdToStyleSheetIds[header.frameId].length) { |
| delete frameIdToStyleSheetIds[header.frameId]; |
| if (!Object.keys(this._styleSheetIdsForURL[url]).length) |
| delete this._styleSheetIdsForURL[url]; |
| } |
| this._styleLoader.reset(); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, header); |
| }, |
| |
| /** |
| * @param {string} url |
| * @return {!Array.<!CSSAgent.StyleSheetId>} |
| */ |
| styleSheetIdsForURL: function(url) |
| { |
| var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url]; |
| if (!frameIdToStyleSheetIds) |
| return []; |
| |
| var result = []; |
| for (var frameId in frameIdToStyleSheetIds) |
| result = result.concat(frameIdToStyleSheetIds[frameId]); |
| return result; |
| }, |
| |
| /** |
| * @param {string} url |
| * @return {!Object.<!PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>} |
| */ |
| styleSheetIdsByFrameIdForURL: function(url) |
| { |
| var styleSheetIdsForFrame = this._styleSheetIdsForURL[url]; |
| if (!styleSheetIdsForFrame) |
| return {}; |
| return styleSheetIdsForFrame; |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| _namedFlowCreated: function(namedFlowPayload) |
| { |
| var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload); |
| var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId]; |
| |
| if (!namedFlowCollection) |
| return; |
| |
| namedFlowCollection._appendNamedFlow(namedFlow); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowCreated, namedFlow); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} documentNodeId |
| * @param {string} flowName |
| */ |
| _namedFlowRemoved: function(documentNodeId, flowName) |
| { |
| var namedFlowCollection = this._namedFlowCollections[documentNodeId]; |
| |
| if (!namedFlowCollection) |
| return; |
| |
| namedFlowCollection._removeNamedFlow(flowName); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, { documentNodeId: documentNodeId, flowName: flowName }); |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| _regionLayoutUpdated: function(namedFlowPayload) |
| { |
| var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload); |
| var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId]; |
| |
| if (!namedFlowCollection) |
| return; |
| |
| namedFlowCollection._appendNamedFlow(namedFlow); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, namedFlow); |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| _regionOversetChanged: function(namedFlowPayload) |
| { |
| var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload); |
| var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId]; |
| |
| if (!namedFlowCollection) |
| return; |
| |
| namedFlowCollection._appendNamedFlow(namedFlow); |
| this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionOversetChanged, namedFlow); |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} styleSheetId |
| * @param {string} newText |
| * @param {boolean} majorChange |
| * @param {function(?string)} userCallback |
| */ |
| setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback) |
| { |
| function callback(error) |
| { |
| this._pendingCommandsMajorState.pop(); |
| if (!error && majorChange) |
| WebInspector.domAgent.markUndoableState(); |
| |
| if (!error && userCallback) |
| userCallback(error); |
| } |
| this._pendingCommandsMajorState.push(majorChange); |
| CSSAgent.setStyleSheetText(styleSheetId, newText, callback.bind(this)); |
| }, |
| |
| _undoRedoRequested: function() |
| { |
| this._pendingCommandsMajorState.push(true); |
| }, |
| |
| _undoRedoCompleted: function() |
| { |
| this._pendingCommandsMajorState.pop(); |
| }, |
| |
| _mainFrameCreatedOrNavigated: function() |
| { |
| this._resetStyleSheets(); |
| }, |
| |
| _resetStyleSheets: function() |
| { |
| /** @type {!Object.<string, !Object.<!PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>>} */ |
| this._styleSheetIdsForURL = {}; |
| /** @type {!Object.<!CSSAgent.StyleSheetId, !WebInspector.CSSStyleSheetHeader>} */ |
| this._styleSheetIdToHeader = {}; |
| }, |
| |
| _resetNamedFlowCollections: function() |
| { |
| this._namedFlowCollections = {}; |
| }, |
| |
| updateLocations: function() |
| { |
| var headers = Object.values(this._styleSheetIdToHeader); |
| for (var i = 0; i < headers.length; ++i) |
| headers[i].updateLocations(); |
| }, |
| |
| /** |
| * @param {?CSSAgent.StyleSheetId} styleSheetId |
| * @param {!WebInspector.CSSLocation} rawLocation |
| * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate |
| * @return {?WebInspector.LiveLocation} |
| */ |
| createLiveLocation: function(styleSheetId, rawLocation, updateDelegate) |
| { |
| if (!rawLocation) |
| return null; |
| var header = styleSheetId ? this.styleSheetHeaderForId(styleSheetId) : null; |
| return new WebInspector.CSSStyleModel.LiveLocation(this, header, rawLocation, updateDelegate); |
| }, |
| |
| /** |
| * @param {!WebInspector.CSSLocation} rawLocation |
| * @return {?WebInspector.UILocation} |
| */ |
| rawLocationToUILocation: function(rawLocation) |
| { |
| var frameIdToSheetIds = this._styleSheetIdsForURL[rawLocation.url]; |
| if (!frameIdToSheetIds) |
| return null; |
| var styleSheetIds = []; |
| for (var frameId in frameIdToSheetIds) |
| styleSheetIds = styleSheetIds.concat(frameIdToSheetIds[frameId]); |
| var uiLocation; |
| for (var i = 0; !uiLocation && i < styleSheetIds.length; ++i) { |
| var header = this.styleSheetHeaderForId(styleSheetIds[i]); |
| console.assert(header); |
| uiLocation = header.rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber); |
| } |
| return uiLocation || null; |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.LiveLocation} |
| * @param {!WebInspector.CSSStyleModel} model |
| * @param {?WebInspector.CSSStyleSheetHeader} header |
| * @param {!WebInspector.CSSLocation} rawLocation |
| * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate |
| */ |
| WebInspector.CSSStyleModel.LiveLocation = function(model, header, rawLocation, updateDelegate) |
| { |
| WebInspector.LiveLocation.call(this, rawLocation, updateDelegate); |
| this._model = model; |
| if (!header) |
| this._clearStyleSheet(); |
| else |
| this._setStyleSheet(header); |
| } |
| |
| WebInspector.CSSStyleModel.LiveLocation.prototype = { |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _styleSheetAdded: function(event) |
| { |
| console.assert(!this._header); |
| var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data); |
| if (header.sourceURL && header.sourceURL === this.rawLocation().url) |
| this._setStyleSheet(header); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _styleSheetRemoved: function(event) |
| { |
| console.assert(this._header); |
| var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data); |
| if (this._header !== header) |
| return; |
| this._header._removeLocation(this); |
| this._clearStyleSheet(); |
| }, |
| |
| /** |
| * @param {!WebInspector.CSSStyleSheetHeader} header |
| */ |
| _setStyleSheet: function(header) |
| { |
| this._header = header; |
| this._header.addLiveLocation(this); |
| this._model.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this); |
| this._model.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this); |
| }, |
| |
| _clearStyleSheet: function() |
| { |
| delete this._header; |
| this._model.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this); |
| this._model.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this); |
| }, |
| |
| /** |
| * @return {?WebInspector.UILocation} |
| */ |
| uiLocation: function() |
| { |
| var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation()); |
| if (this._header) |
| return this._header.rawLocationToUILocation(cssLocation.lineNumber, cssLocation.columnNumber); |
| var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(cssLocation.url); |
| if (!uiSourceCode) |
| return null; |
| return new WebInspector.UILocation(uiSourceCode, cssLocation.lineNumber, cssLocation.columnNumber); |
| }, |
| |
| dispose: function() |
| { |
| WebInspector.LiveLocation.prototype.dispose.call(this); |
| this._model.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this); |
| this._model.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this); |
| }, |
| |
| __proto__: WebInspector.LiveLocation.prototype |
| } |
| |
| /** |
| * @constructor |
| * @implements {WebInspector.RawLocation} |
| * @param {string} url |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| */ |
| WebInspector.CSSLocation = function(url, lineNumber, columnNumber) |
| { |
| this.url = url; |
| this.lineNumber = lineNumber; |
| this.columnNumber = columnNumber || 0; |
| } |
| |
| /** |
| * @constructor |
| * @param {!CSSAgent.CSSStyle} payload |
| */ |
| WebInspector.CSSStyleDeclaration = function(payload) |
| { |
| this.id = payload.styleId; |
| this.width = payload.width; |
| this.height = payload.height; |
| this.range = payload.range; |
| this._shorthandValues = WebInspector.CSSStyleDeclaration.buildShorthandValueMap(payload.shorthandEntries); |
| this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty } |
| this._allProperties = []; // ALL properties: [ CSSProperty ] |
| this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty } |
| var payloadPropertyCount = payload.cssProperties.length; |
| |
| var propertyIndex = 0; |
| for (var i = 0; i < payloadPropertyCount; ++i) { |
| var property = WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]); |
| this._allProperties.push(property); |
| if (property.disabled) |
| this.__disabledProperties[i] = property; |
| if (!property.active && !property.styleBased) |
| continue; |
| var name = property.name; |
| this[propertyIndex] = name; |
| this._livePropertyMap[name] = property; |
| ++propertyIndex; |
| } |
| this.length = propertyIndex; |
| if ("cssText" in payload) |
| this.cssText = payload.cssText; |
| } |
| |
| /** |
| * @param {!Array.<!CSSAgent.ShorthandEntry>} shorthandEntries |
| * @return {!Object} |
| */ |
| WebInspector.CSSStyleDeclaration.buildShorthandValueMap = function(shorthandEntries) |
| { |
| var result = {}; |
| for (var i = 0; i < shorthandEntries.length; ++i) |
| result[shorthandEntries[i].name] = shorthandEntries[i].value; |
| return result; |
| } |
| |
| /** |
| * @param {!CSSAgent.CSSStyle} payload |
| * @return {!WebInspector.CSSStyleDeclaration} |
| */ |
| WebInspector.CSSStyleDeclaration.parsePayload = function(payload) |
| { |
| return new WebInspector.CSSStyleDeclaration(payload); |
| } |
| |
| /** |
| * @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} payload |
| * @return {!WebInspector.CSSStyleDeclaration} |
| */ |
| WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(payload) |
| { |
| var newPayload = /** @type {!CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" }); |
| if (payload) |
| newPayload.cssProperties = /** @type {!Array.<!CSSAgent.CSSProperty>} */ (payload); |
| |
| return new WebInspector.CSSStyleDeclaration(newPayload); |
| } |
| |
| WebInspector.CSSStyleDeclaration.prototype = { |
| get allProperties() |
| { |
| return this._allProperties; |
| }, |
| |
| /** |
| * @param {string} name |
| * @return {?WebInspector.CSSProperty} |
| */ |
| getLiveProperty: function(name) |
| { |
| return this._livePropertyMap[name] || null; |
| }, |
| |
| /** |
| * @param {string} name |
| * @return {string} |
| */ |
| getPropertyValue: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.value : ""; |
| }, |
| |
| /** |
| * @param {string} name |
| * @return {string} |
| */ |
| getPropertyPriority: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.priority : ""; |
| }, |
| |
| /** |
| * @param {string} name |
| * @return {boolean} |
| */ |
| isPropertyImplicit: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.implicit : ""; |
| }, |
| |
| /** |
| * @param {string} name |
| * @return {!Array.<!WebInspector.CSSProperty>} |
| */ |
| longhandProperties: function(name) |
| { |
| var longhands = WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(name); |
| var result = []; |
| for (var i = 0; longhands && i < longhands.length; ++i) { |
| var property = this._livePropertyMap[longhands[i]]; |
| if (property) |
| result.push(property); |
| } |
| return result; |
| }, |
| |
| /** |
| * @param {string} shorthandProperty |
| * @return {string} |
| */ |
| shorthandValue: function(shorthandProperty) |
| { |
| return this._shorthandValues[shorthandProperty]; |
| }, |
| |
| /** |
| * @param {number} index |
| * @return {?WebInspector.CSSProperty} |
| */ |
| propertyAt: function(index) |
| { |
| return (index < this.allProperties.length) ? this.allProperties[index] : null; |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| pastLastSourcePropertyIndex: function() |
| { |
| for (var i = this.allProperties.length - 1; i >= 0; --i) { |
| var property = this.allProperties[i]; |
| if (property.active || property.disabled) |
| return i + 1; |
| } |
| return 0; |
| }, |
| |
| /** |
| * @param {number=} index |
| */ |
| newBlankProperty: function(index) |
| { |
| index = (typeof index === "undefined") ? this.pastLastSourcePropertyIndex() : index; |
| return new WebInspector.CSSProperty(this, index, "", "", "", "active", true, false, ""); |
| }, |
| |
| /** |
| * @param {number} index |
| * @param {string} name |
| * @param {string} value |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| insertPropertyAt: function(index, name, value, userCallback) |
| { |
| /** |
| * @param {?string} error |
| * @param {!CSSAgent.CSSStyle} payload |
| */ |
| function callback(error, payload) |
| { |
| WebInspector.cssModel._pendingCommandsMajorState.pop(); |
| if (!userCallback) |
| return; |
| |
| if (error) { |
| console.error(error); |
| userCallback(null); |
| } else |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload)); |
| } |
| |
| if (!this.id) |
| throw "No style id"; |
| |
| WebInspector.cssModel._pendingCommandsMajorState.push(true); |
| CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(this)); |
| }, |
| |
| /** |
| * @param {string} name |
| * @param {string} value |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| appendProperty: function(name, value, userCallback) |
| { |
| this.insertPropertyAt(this.allProperties.length, name, value, userCallback); |
| }, |
| |
| /** |
| * @param {string} text |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| setText: function(text, userCallback) |
| { |
| /** |
| * @param {?string} error |
| * @param {!CSSAgent.CSSStyle} payload |
| */ |
| function callback(error, payload) |
| { |
| WebInspector.cssModel._pendingCommandsMajorState.pop(); |
| if (!userCallback) |
| return; |
| |
| if (error) { |
| console.error(error); |
| userCallback(null); |
| } else |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload)); |
| } |
| |
| if (!this.id) |
| throw "No style id"; |
| |
| if (typeof this.cssText === "undefined") { |
| userCallback(null); |
| return; |
| } |
| |
| WebInspector.cssModel._pendingCommandsMajorState.push(true); |
| CSSAgent.setStyleText(this.id, text, callback); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {!CSSAgent.CSSRule} payload |
| * @param {!Array.<number>=} matchingSelectors |
| */ |
| WebInspector.CSSRule = function(payload, matchingSelectors) |
| { |
| this.id = payload.ruleId; |
| if (matchingSelectors) |
| this.matchingSelectors = matchingSelectors; |
| this.selectors = payload.selectorList.selectors; |
| this.selectorText = this.selectors.select("value").join(", "); |
| |
| var firstRange = this.selectors[0].range; |
| if (firstRange) { |
| var lastRange = this.selectors.peekLast().range; |
| this.selectorRange = new WebInspector.TextRange(firstRange.startLine, firstRange.startColumn, lastRange.endLine, lastRange.endColumn); |
| } |
| this.sourceURL = payload.sourceURL; |
| this.origin = payload.origin; |
| this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style); |
| this.style.parentRule = this; |
| if (payload.media) |
| this.media = WebInspector.CSSMedia.parseMediaArrayPayload(payload.media); |
| this._setRawLocationAndFrameId(); |
| } |
| |
| /** |
| * @param {!CSSAgent.CSSRule} payload |
| * @param {!Array.<number>=} matchingIndices |
| * @return {!WebInspector.CSSRule} |
| */ |
| WebInspector.CSSRule.parsePayload = function(payload, matchingIndices) |
| { |
| return new WebInspector.CSSRule(payload, matchingIndices); |
| } |
| |
| WebInspector.CSSRule.prototype = { |
| _setRawLocationAndFrameId: function() |
| { |
| if (!this.id) |
| return; |
| var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId); |
| this.frameId = styleSheetHeader.frameId; |
| var url = styleSheetHeader.resourceURL(); |
| if (!url) |
| return; |
| this.rawLocation = new WebInspector.CSSLocation(url, this.lineNumberInSource(0), this.columnNumberInSource(0)); |
| }, |
| |
| /** |
| * @return {string} |
| */ |
| resourceURL: function() |
| { |
| if (!this.id) |
| return ""; |
| var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId); |
| return styleSheetHeader.resourceURL(); |
| }, |
| |
| /** |
| * @param {number} selectorIndex |
| * @return {number} |
| */ |
| lineNumberInSource: function(selectorIndex) |
| { |
| var selector = this.selectors[selectorIndex]; |
| if (!selector || !selector.range) |
| return 0; |
| var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId); |
| return styleSheetHeader.lineNumberInSource(selector.range.startLine); |
| }, |
| |
| /** |
| * @param {number} selectorIndex |
| * @return {number|undefined} |
| */ |
| columnNumberInSource: function(selectorIndex) |
| { |
| var selector = this.selectors[selectorIndex]; |
| if (!selector || !selector.range) |
| return undefined; |
| var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId); |
| console.assert(styleSheetHeader); |
| return styleSheetHeader.columnNumberInSource(selector.range.startLine, selector.range.startColumn); |
| }, |
| |
| get isUserAgent() |
| { |
| return this.origin === "user-agent"; |
| }, |
| |
| get isUser() |
| { |
| return this.origin === "user"; |
| }, |
| |
| get isViaInspector() |
| { |
| return this.origin === "inspector"; |
| }, |
| |
| get isRegular() |
| { |
| return this.origin === "regular"; |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {?WebInspector.CSSStyleDeclaration} ownerStyle |
| * @param {number} index |
| * @param {string} name |
| * @param {string} value |
| * @param {?string} priority |
| * @param {string} status |
| * @param {boolean} parsedOk |
| * @param {boolean} implicit |
| * @param {?string=} text |
| * @param {!CSSAgent.SourceRange=} range |
| */ |
| WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text, range) |
| { |
| this.ownerStyle = ownerStyle; |
| this.index = index; |
| this.name = name; |
| this.value = value; |
| this.priority = priority; |
| this.status = status; |
| this.parsedOk = parsedOk; |
| this.implicit = implicit; |
| this.text = text; |
| this.range = range; |
| } |
| |
| /** |
| * @param {?WebInspector.CSSStyleDeclaration} ownerStyle |
| * @param {number} index |
| * @param {!CSSAgent.CSSProperty} payload |
| * @return {!WebInspector.CSSProperty} |
| */ |
| WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload) |
| { |
| // The following default field values are used in the payload: |
| // priority: "" |
| // parsedOk: true |
| // implicit: false |
| // status: "style" |
| var result = new WebInspector.CSSProperty( |
| ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range); |
| return result; |
| } |
| |
| WebInspector.CSSProperty.prototype = { |
| get propertyText() |
| { |
| if (this.text !== undefined) |
| return this.text; |
| |
| if (this.name === "") |
| return ""; |
| return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";"; |
| }, |
| |
| get isLive() |
| { |
| return this.active || this.styleBased; |
| }, |
| |
| get active() |
| { |
| return this.status === "active"; |
| }, |
| |
| get styleBased() |
| { |
| return this.status === "style"; |
| }, |
| |
| get inactive() |
| { |
| return this.status === "inactive"; |
| }, |
| |
| get disabled() |
| { |
| return this.status === "disabled"; |
| }, |
| |
| /** |
| * Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText. |
| * |
| * @param {string} propertyText |
| * @param {boolean} majorChange |
| * @param {boolean} overwrite |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| setText: function(propertyText, majorChange, overwrite, userCallback) |
| { |
| /** |
| * @param {?WebInspector.CSSStyleDeclaration} style |
| */ |
| function enabledCallback(style) |
| { |
| if (userCallback) |
| userCallback(style); |
| } |
| |
| /** |
| * @param {?string} error |
| * @param {!CSSAgent.CSSStyle} stylePayload |
| */ |
| function callback(error, stylePayload) |
| { |
| WebInspector.cssModel._pendingCommandsMajorState.pop(); |
| if (!error) { |
| if (majorChange) |
| WebInspector.domAgent.markUndoableState(); |
| this.text = propertyText; |
| var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload); |
| var newProperty = style.allProperties[this.index]; |
| |
| if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) { |
| newProperty.setDisabled(false, enabledCallback); |
| return; |
| } |
| |
| if (userCallback) |
| userCallback(style); |
| } else { |
| if (userCallback) |
| userCallback(null); |
| } |
| } |
| |
| if (!this.ownerStyle) |
| throw "No ownerStyle for property"; |
| |
| if (!this.ownerStyle.id) |
| throw "No owner style id"; |
| |
| // An index past all the properties adds a new property to the style. |
| WebInspector.cssModel._pendingCommandsMajorState.push(majorChange); |
| CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, overwrite, callback.bind(this)); |
| }, |
| |
| /** |
| * @param {string} newValue |
| * @param {boolean} majorChange |
| * @param {boolean} overwrite |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| setValue: function(newValue, majorChange, overwrite, userCallback) |
| { |
| var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";" |
| this.setText(text, majorChange, overwrite, userCallback); |
| }, |
| |
| /** |
| * @param {boolean} disabled |
| * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback |
| */ |
| setDisabled: function(disabled, userCallback) |
| { |
| if (!this.ownerStyle && userCallback) |
| userCallback(null); |
| if (disabled === this.disabled && userCallback) |
| userCallback(this.ownerStyle); |
| |
| /** |
| * @param {?string} error |
| * @param {!CSSAgent.CSSStyle} stylePayload |
| */ |
| function callback(error, stylePayload) |
| { |
| WebInspector.cssModel._pendingCommandsMajorState.pop(); |
| if (error) { |
| if (userCallback) |
| userCallback(null); |
| return; |
| } |
| WebInspector.domAgent.markUndoableState(); |
| if (userCallback) { |
| var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload); |
| userCallback(style); |
| } |
| } |
| |
| if (!this.ownerStyle.id) |
| throw "No owner style id"; |
| |
| WebInspector.cssModel._pendingCommandsMajorState.push(false); |
| CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this)); |
| }, |
| |
| /** |
| * @param {boolean} forName |
| * @return {?WebInspector.UILocation} |
| */ |
| uiLocation: function(forName) |
| { |
| if (!this.range || !this.ownerStyle || !this.ownerStyle.parentRule) |
| return null; |
| |
| var url = this.ownerStyle.parentRule.resourceURL(); |
| if (!url) |
| return null; |
| |
| var range = this.range; |
| var line = forName ? range.startLine : range.endLine; |
| // End of range is exclusive, so subtract 1 from the end offset. |
| var column = forName ? range.startColumn : range.endColumn - (this.text && this.text.endsWith(";") ? 2 : 1); |
| var rawLocation = new WebInspector.CSSLocation(url, line, column); |
| return WebInspector.cssModel.rawLocationToUILocation(rawLocation); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {!CSSAgent.CSSMedia} payload |
| */ |
| WebInspector.CSSMedia = function(payload) |
| { |
| this.text = payload.text; |
| this.source = payload.source; |
| this.sourceURL = payload.sourceURL || ""; |
| this.range = payload.range ? WebInspector.TextRange.fromObject(payload.range) : null; |
| this.parentStyleSheetId = payload.parentStyleSheetId; |
| } |
| |
| WebInspector.CSSMedia.Source = { |
| LINKED_SHEET: "linkedSheet", |
| INLINE_SHEET: "inlineSheet", |
| MEDIA_RULE: "mediaRule", |
| IMPORT_RULE: "importRule" |
| }; |
| |
| /** |
| * @param {!CSSAgent.CSSMedia} payload |
| * @return {!WebInspector.CSSMedia} |
| */ |
| WebInspector.CSSMedia.parsePayload = function(payload) |
| { |
| return new WebInspector.CSSMedia(payload); |
| } |
| |
| /** |
| * @param {!Array.<!CSSAgent.CSSMedia>} payload |
| * @return {!Array.<!WebInspector.CSSMedia>} |
| */ |
| WebInspector.CSSMedia.parseMediaArrayPayload = function(payload) |
| { |
| var result = []; |
| for (var i = 0; i < payload.length; ++i) |
| result.push(WebInspector.CSSMedia.parsePayload(payload[i])); |
| return result; |
| } |
| |
| WebInspector.CSSMedia.prototype = { |
| /** |
| * @return {number|undefined} |
| */ |
| lineNumberInSource: function() |
| { |
| if (!this.range) |
| return undefined; |
| var header = this.header(); |
| if (!header) |
| return undefined; |
| return header.lineNumberInSource(this.range.startLine); |
| }, |
| |
| /** |
| * @return {number|undefined} |
| */ |
| columnNumberInSource: function() |
| { |
| if (!this.range) |
| return undefined; |
| var header = this.header(); |
| if (!header) |
| return undefined; |
| return header.columnNumberInSource(this.range.startLine, this.range.startColumn); |
| }, |
| |
| /** |
| * @return {?WebInspector.CSSStyleSheetHeader} |
| */ |
| header: function() |
| { |
| return this.parentStyleSheetId ? WebInspector.cssModel.styleSheetHeaderForId(this.parentStyleSheetId) : null; |
| } |
| } |
| |
| /** |
| * @constructor |
| * @implements {WebInspector.ContentProvider} |
| * @param {!CSSAgent.CSSStyleSheetHeader} payload |
| */ |
| WebInspector.CSSStyleSheetHeader = function(payload) |
| { |
| this.id = payload.styleSheetId; |
| this.frameId = payload.frameId; |
| this.sourceURL = payload.sourceURL; |
| this.hasSourceURL = !!payload.hasSourceURL; |
| this.sourceMapURL = payload.sourceMapURL; |
| this.origin = payload.origin; |
| this.title = payload.title; |
| this.disabled = payload.disabled; |
| this.isInline = payload.isInline; |
| this.startLine = payload.startLine; |
| this.startColumn = payload.startColumn; |
| /** @type {!Set.<!WebInspector.CSSStyleModel.LiveLocation>} */ |
| this._locations = new Set(); |
| /** @type {!Array.<!WebInspector.SourceMapping>} */ |
| this._sourceMappings = []; |
| } |
| |
| WebInspector.CSSStyleSheetHeader.prototype = { |
| /** |
| * @return {string} |
| */ |
| resourceURL: function() |
| { |
| return this.origin === "inspector" ? this._viaInspectorResourceURL() : this.sourceURL; |
| }, |
| |
| /** |
| * @param {!WebInspector.CSSStyleModel.LiveLocation} location |
| */ |
| addLiveLocation: function(location) |
| { |
| this._locations.add(location); |
| location.update(); |
| }, |
| |
| updateLocations: function() |
| { |
| var items = this._locations.items(); |
| for (var i = 0; i < items.length; ++i) |
| items[i].update(); |
| }, |
| |
| /** |
| * @param {!WebInspector.CSSStyleModel.LiveLocation} location |
| */ |
| _removeLocation: function(location) |
| { |
| this._locations.remove(location); |
| }, |
| |
| /** |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| * @return {?WebInspector.UILocation} |
| */ |
| rawLocationToUILocation: function(lineNumber, columnNumber) |
| { |
| var uiLocation; |
| var rawLocation = new WebInspector.CSSLocation(this.resourceURL(), lineNumber, columnNumber); |
| for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i) |
| uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation); |
| return uiLocation ? uiLocation.uiSourceCode.overrideLocation(uiLocation) : null; |
| }, |
| |
| /** |
| * @param {!WebInspector.SourceMapping} sourceMapping |
| */ |
| pushSourceMapping: function(sourceMapping) |
| { |
| this._sourceMappings.push(sourceMapping); |
| this.updateLocations(); |
| }, |
| |
| /** |
| * @return {string} |
| */ |
| _key: function() |
| { |
| return this.frameId + ":" + this.resourceURL(); |
| }, |
| |
| /** |
| * @return {string} |
| */ |
| _viaInspectorResourceURL: function() |
| { |
| var frame = WebInspector.resourceTreeModel.frameForId(this.frameId); |
| console.assert(frame); |
| var parsedURL = new WebInspector.ParsedURL(frame.url); |
| var fakeURL = "inspector://" + parsedURL.host + parsedURL.folderPathComponents; |
| if (!fakeURL.endsWith("/")) |
| fakeURL += "/"; |
| fakeURL += "inspector-stylesheet"; |
| return fakeURL; |
| }, |
| |
| /** |
| * @param {number} lineNumberInStyleSheet |
| * @return {number} |
| */ |
| lineNumberInSource: function(lineNumberInStyleSheet) |
| { |
| return this.startLine + lineNumberInStyleSheet; |
| }, |
| |
| /** |
| * @param {number} lineNumberInStyleSheet |
| * @param {number} columnNumberInStyleSheet |
| * @return {number|undefined} |
| */ |
| columnNumberInSource: function(lineNumberInStyleSheet, columnNumberInStyleSheet) |
| { |
| return (lineNumberInStyleSheet ? 0 : this.startColumn) + columnNumberInStyleSheet; |
| }, |
| |
| /** |
| * @override |
| */ |
| contentURL: function() |
| { |
| return this.resourceURL(); |
| }, |
| |
| /** |
| * @override |
| */ |
| contentType: function() |
| { |
| return WebInspector.resourceTypes.Stylesheet; |
| }, |
| |
| /** |
| * @override |
| */ |
| requestContent: function(callback) |
| { |
| CSSAgent.getStyleSheetText(this.id, textCallback.bind(this)); |
| |
| function textCallback(error, text) |
| { |
| if (error) { |
| WebInspector.log("Failed to get text for stylesheet " + this.id + ": " + error); |
| text = ""; |
| // Fall through. |
| } |
| callback(text); |
| } |
| }, |
| |
| /** |
| * @override |
| */ |
| searchInContent: function(query, caseSensitive, isRegex, callback) |
| { |
| function performSearch(content) |
| { |
| callback(WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex)); |
| } |
| |
| // searchInContent should call back later. |
| this.requestContent(performSearch); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {!CSSAgent.CSSStyleSheetBody} payload |
| */ |
| WebInspector.CSSStyleSheet = function(payload) |
| { |
| this.id = payload.styleSheetId; |
| this.rules = []; |
| this.styles = {}; |
| for (var i = 0; i < payload.rules.length; ++i) { |
| var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]); |
| this.rules.push(rule); |
| if (rule.style) |
| this.styles[rule.style.id] = rule.style; |
| } |
| if ("text" in payload) |
| this._text = payload.text; |
| } |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} styleSheetId |
| * @param {function(?WebInspector.CSSStyleSheet)} userCallback |
| */ |
| WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback) |
| { |
| /** |
| * @param {?string} error |
| * @param {!CSSAgent.CSSStyleSheetBody} styleSheetPayload |
| */ |
| function callback(error, styleSheetPayload) |
| { |
| if (error) |
| userCallback(null); |
| else |
| userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload)); |
| } |
| CSSAgent.getStyleSheet(styleSheetId, callback.bind(this)); |
| } |
| |
| WebInspector.CSSStyleSheet.prototype = { |
| /** |
| * @return {string|undefined} |
| */ |
| getText: function() |
| { |
| return this._text; |
| }, |
| |
| /** |
| * @param {string} newText |
| * @param {boolean} majorChange |
| * @param {function(?string)=} userCallback |
| */ |
| setText: function(newText, majorChange, userCallback) |
| { |
| /** |
| * @param {?string} error |
| */ |
| function callback(error) |
| { |
| if (!error) |
| WebInspector.domAgent.markUndoableState(); |
| |
| WebInspector.cssModel._pendingCommandsMajorState.pop(); |
| if (userCallback) |
| userCallback(error); |
| } |
| |
| WebInspector.cssModel._pendingCommandsMajorState.push(majorChange); |
| CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this)); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @implements {CSSAgent.Dispatcher} |
| * @param {!WebInspector.CSSStyleModel} cssModel |
| */ |
| WebInspector.CSSDispatcher = function(cssModel) |
| { |
| this._cssModel = cssModel; |
| } |
| |
| WebInspector.CSSDispatcher.prototype = { |
| mediaQueryResultChanged: function() |
| { |
| this._cssModel.mediaQueryResultChanged(); |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} styleSheetId |
| */ |
| styleSheetChanged: function(styleSheetId) |
| { |
| this._cssModel._fireStyleSheetChanged(styleSheetId); |
| }, |
| |
| /** |
| * @param {!CSSAgent.CSSStyleSheetHeader} header |
| */ |
| styleSheetAdded: function(header) |
| { |
| this._cssModel._styleSheetAdded(header); |
| }, |
| |
| /** |
| * @param {!CSSAgent.StyleSheetId} id |
| */ |
| styleSheetRemoved: function(id) |
| { |
| this._cssModel._styleSheetRemoved(id); |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| namedFlowCreated: function(namedFlowPayload) |
| { |
| this._cssModel._namedFlowCreated(namedFlowPayload); |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} documentNodeId |
| * @param {string} flowName |
| */ |
| namedFlowRemoved: function(documentNodeId, flowName) |
| { |
| this._cssModel._namedFlowRemoved(documentNodeId, flowName); |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| regionLayoutUpdated: function(namedFlowPayload) |
| { |
| this._cssModel._regionLayoutUpdated(namedFlowPayload); |
| }, |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} namedFlowPayload |
| */ |
| regionOversetChanged: function(namedFlowPayload) |
| { |
| this._cssModel._regionOversetChanged(namedFlowPayload); |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {!CSSAgent.NamedFlow} payload |
| */ |
| WebInspector.NamedFlow = function(payload) |
| { |
| this.documentNodeId = payload.documentNodeId; |
| this.name = payload.name; |
| this.overset = payload.overset; |
| this.content = payload.content; |
| this.regions = payload.regions; |
| } |
| |
| /** |
| * @param {!CSSAgent.NamedFlow} payload |
| * @return {!WebInspector.NamedFlow} |
| */ |
| WebInspector.NamedFlow.parsePayload = function(payload) |
| { |
| return new WebInspector.NamedFlow(payload); |
| } |
| |
| /** |
| * @constructor |
| * @param {!Array.<!CSSAgent.NamedFlow>} payload |
| */ |
| WebInspector.NamedFlowCollection = function(payload) |
| { |
| /** @type {!Object.<string, !WebInspector.NamedFlow>} */ |
| this.namedFlowMap = {}; |
| |
| for (var i = 0; i < payload.length; ++i) { |
| var namedFlow = WebInspector.NamedFlow.parsePayload(payload[i]); |
| this.namedFlowMap[namedFlow.name] = namedFlow; |
| } |
| } |
| |
| WebInspector.NamedFlowCollection.prototype = { |
| /** |
| * @param {!WebInspector.NamedFlow} namedFlow |
| */ |
| _appendNamedFlow: function(namedFlow) |
| { |
| this.namedFlowMap[namedFlow.name] = namedFlow; |
| }, |
| |
| /** |
| * @param {string} flowName |
| */ |
| _removeNamedFlow: function(flowName) |
| { |
| delete this.namedFlowMap[flowName]; |
| }, |
| |
| /** |
| * @param {string} flowName |
| * @return {?WebInspector.NamedFlow} |
| */ |
| flowByName: function(flowName) |
| { |
| var namedFlow = this.namedFlowMap[flowName]; |
| |
| if (!namedFlow) |
| return null; |
| return namedFlow; |
| } |
| } |
| |
| /** |
| * @constructor |
| * @param {!WebInspector.CSSStyleModel} cssModel |
| */ |
| WebInspector.CSSStyleModel.ComputedStyleLoader = function(cssModel) |
| { |
| this._cssModel = cssModel; |
| /** @type {!Object.<*, !Array.<function(?WebInspector.CSSStyleDeclaration)>>} */ |
| this._nodeIdToCallbackData = {}; |
| } |
| |
| WebInspector.CSSStyleModel.ComputedStyleLoader.prototype = { |
| reset: function() |
| { |
| for (var nodeId in this._nodeIdToCallbackData) { |
| var callbacks = this._nodeIdToCallbackData[nodeId]; |
| for (var i = 0; i < callbacks.length; ++i) |
| callbacks[i](null); |
| } |
| this._nodeIdToCallbackData = {}; |
| }, |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback |
| */ |
| getComputedStyle: function(nodeId, userCallback) |
| { |
| if (this._nodeIdToCallbackData[nodeId]) { |
| this._nodeIdToCallbackData[nodeId].push(userCallback); |
| return; |
| } |
| |
| this._nodeIdToCallbackData[nodeId] = [userCallback]; |
| |
| CSSAgent.getComputedStyleForNode(nodeId, resultCallback.bind(this, nodeId)); |
| |
| /** |
| * @param {!DOMAgent.NodeId} nodeId |
| * @param {?Protocol.Error} error |
| * @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} computedPayload |
| */ |
| function resultCallback(nodeId, error, computedPayload) |
| { |
| var computedStyle = (error || !computedPayload) ? null : WebInspector.CSSStyleDeclaration.parseComputedStylePayload(computedPayload); |
| var callbacks = this._nodeIdToCallbackData[nodeId]; |
| |
| // The loader has been reset. |
| if (!callbacks) |
| return; |
| |
| delete this._nodeIdToCallbackData[nodeId]; |
| for (var i = 0; i < callbacks.length; ++i) |
| callbacks[i](computedStyle); |
| } |
| } |
| } |
| |
| /** |
| * @type {?WebInspector.CSSStyleModel} |
| */ |
| WebInspector.cssModel = null; |