blob: 18eade4a6847ec5ced2ab80c55364d31cd49c8e4 [file] [log] [blame]
/*
* 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.SDKModel}
* @param {!WebInspector.Target} target
*/
WebInspector.CSSStyleModel = function(target)
{
WebInspector.SDKModel.call(this, WebInspector.CSSStyleModel, target);
this._domModel = target.domModel;
this._agent = target.cssAgent();
this._pendingCommandsMajorState = [];
this._styleLoader = new WebInspector.CSSStyleModel.ComputedStyleLoader(this);
this._domModel.addEventListener(WebInspector.DOMModel.Events.UndoRedoRequested, this._undoRedoRequested, this);
this._domModel.addEventListener(WebInspector.DOMModel.Events.UndoRedoCompleted, this._undoRedoCompleted, this);
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
target.registerCSSDispatcher(new WebInspector.CSSDispatcher(this));
this._agent.enable(this._wasEnabled.bind(this));
/** @type {!StringMap.<!WebInspector.CSSStyleSheetHeader>} */
this._styleSheetIdToHeader = new StringMap();
/** @type {!StringMap.<!Object.<!PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>>} */
this._styleSheetIdsForURL = new StringMap();
if (WebInspector.experimentsSettings.disableAgentsWhenProfile.isEnabled())
WebInspector.profilingLock().addEventListener(WebInspector.Lock.Events.StateChanged, this._profilingStateChanged, this);
}
WebInspector.CSSStyleModel.PseudoStatePropertyName = "pseudoState";
/**
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!Array.<!CSSAgent.RuleMatch>|undefined} matchArray
* @return {!Array.<!WebInspector.CSSRule>}
*/
WebInspector.CSSStyleModel.parseRuleMatchArrayPayload = function(cssModel, matchArray)
{
if (!matchArray)
return [];
var result = [];
for (var i = 0; i < matchArray.length; ++i)
result.push(WebInspector.CSSRule.parsePayload(cssModel, matchArray[i].rule, matchArray[i].matchingSelectors));
return result;
}
WebInspector.CSSStyleModel.Events = {
ModelWasEnabled: "ModelWasEnabled",
StyleSheetAdded: "StyleSheetAdded",
StyleSheetChanged: "StyleSheetChanged",
StyleSheetRemoved: "StyleSheetRemoved",
MediaQueryResultChanged: "MediaQueryResultChanged",
}
WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"];
WebInspector.CSSStyleModel.prototype = {
_profilingStateChanged: function()
{
if (WebInspector.profilingLock().isAcquired()) {
this._agent.disable();
this._isEnabled = false;
this._resetStyleSheets();
} else {
this._agent.enable(this._wasEnabled.bind(this));
}
},
/**
* @param {function(!Array.<!WebInspector.CSSMedia>)} userCallback
*/
getMediaQueries: function(userCallback)
{
/**
* @param {?Protocol.Error} error
* @param {?Array.<!CSSAgent.CSSMedia>} payload
* @this {!WebInspector.CSSStyleModel}
*/
function callback(error, payload)
{
var models = [];
if (!error && payload)
models = WebInspector.CSSMedia.parseMediaArrayPayload(this, payload);
userCallback(models);
}
this._agent.getMediaQueries(callback.bind(this));
},
/**
* @return {boolean}
*/
isEnabled: function()
{
return this._isEnabled;
},
_wasEnabled: function()
{
this._isEnabled = true;
this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.ModelWasEnabled);
},
/**
* @param {!DOMAgent.NodeId} nodeId
* @param {boolean} excludePseudo
* @param {boolean} excludeInherited
* @param {function(?*)} userCallback
*/
getMatchedStylesAsync: function(nodeId, excludePseudo, excludeInherited, userCallback)
{
/**
* @param {function(?*)} userCallback
* @param {?Protocol.Error} error
* @param {!Array.<!CSSAgent.RuleMatch>=} matchedPayload
* @param {!Array.<!CSSAgent.PseudoIdMatches>=} pseudoPayload
* @param {!Array.<!CSSAgent.InheritedStyleEntry>=} inheritedPayload
* @this {WebInspector.CSSStyleModel}
*/
function callback(userCallback, error, matchedPayload, pseudoPayload, inheritedPayload)
{
if (error) {
if (userCallback)
userCallback(null);
return;
}
var result = {};
result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(this, 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(this, 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(this, entryPayload.inlineStyle);
if (entryPayload.matchedCSSRules)
entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(this, entryPayload.matchedCSSRules);
result.inherited.push(entry);
}
}
if (userCallback)
userCallback(result);
}
this._agent.getMatchedStylesForNode(nodeId, excludePseudo, excludeInherited, callback.bind(this, 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);
}
this._agent.getPlatformFontsForNode(nodeId, platformFontsCallback);
},
/**
* @return {!Array.<!WebInspector.CSSStyleSheetHeader>}
*/
allStyleSheets: function()
{
var values = this._styleSheetIdToHeader.values();
/**
* @param {!WebInspector.CSSStyleSheetHeader} a
* @param {!WebInspector.CSSStyleSheetHeader} b
* @return {number}
*/
function styleSheetComparator(a, b)
{
if (a.sourceURL < b.sourceURL)
return -1;
else if (a.sourceURL > b.sourceURL)
return 1;
return a.startLine - b.startLine || a.startColumn - b.startColumn;
}
values.sort(styleSheetComparator);
return values;
},
/**
* @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
* @this {WebInspector.CSSStyleModel}
*/
function callback(userCallback, error, inlinePayload, attributesStylePayload)
{
if (error || !inlinePayload)
userCallback(null, null);
else
userCallback(WebInspector.CSSStyleDeclaration.parsePayload(this, inlinePayload), attributesStylePayload ? WebInspector.CSSStyleDeclaration.parsePayload(this, attributesStylePayload) : null);
}
this._agent.getInlineStylesForNode(nodeId, callback.bind(this, userCallback));
},
/**
* @param {!WebInspector.DOMNode} node
* @param {string} pseudoClass
* @param {boolean} enable
* @return {boolean}
*/
forcePseudoState: function(node, pseudoClass, enable)
{
var pseudoClasses = node.getUserProperty(WebInspector.CSSStyleModel.PseudoStatePropertyName) || [];
if (enable) {
if (pseudoClasses.indexOf(pseudoClass) >= 0)
return false;
pseudoClasses.push(pseudoClass);
node.setUserProperty(WebInspector.CSSStyleModel.PseudoStatePropertyName, pseudoClasses);
} else {
if (pseudoClasses.indexOf(pseudoClass) < 0)
return false;
pseudoClasses.remove(pseudoClass);
if (!pseudoClasses.length)
node.removeUserProperty(WebInspector.CSSStyleModel.PseudoStatePropertyName);
}
this._agent.forcePseudoState(node.id, pseudoClasses);
return true;
},
/**
* @param {!CSSAgent.CSSRule} rule
* @param {!DOMAgent.NodeId} nodeId
* @param {string} newSelector
* @param {function(!WebInspector.CSSRule)} successCallback
* @param {function()} failureCallback
*/
setRuleSelector: function(rule, 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
* @this {WebInspector.CSSStyleModel}
*/
function callback(nodeId, successCallback, failureCallback, newSelector, error, rulePayload)
{
this._pendingCommandsMajorState.pop();
if (error) {
failureCallback();
return;
}
this._domModel.markUndoableState();
this._computeMatchingSelectors(rulePayload, nodeId, successCallback, failureCallback);
}
if (!rule.styleSheetId)
throw "No rule stylesheet id";
this._pendingCommandsMajorState.push(true);
this._agent.setRuleSelector(rule.styleSheetId, rule.selectorRange, 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(this, 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(null, i, nodeId, matchingSelectors));
this._domModel.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 {!CSSAgent.StyleSheetId} styleSheetId
* @param {!WebInspector.DOMNode} node
* @param {string} ruleText
* @param {!WebInspector.TextRange} ruleLocation
* @param {function(!WebInspector.CSSRule)} successCallback
* @param {function()} failureCallback
*/
addRule: function(styleSheetId, node, ruleText, ruleLocation, successCallback, failureCallback)
{
this._pendingCommandsMajorState.push(true);
this._agent.addRule(styleSheetId, ruleText, ruleLocation, callback.bind(this));
/**
* @param {?Protocol.Error} error
* @param {!CSSAgent.CSSRule} rulePayload
* @this {WebInspector.CSSStyleModel}
*/
function callback(error, rulePayload)
{
this._pendingCommandsMajorState.pop();
if (error) {
// Invalid syntax for a selector
failureCallback();
} else {
this._domModel.markUndoableState();
this._computeMatchingSelectors(rulePayload, node.id, successCallback, failureCallback);
}
}
},
/**
* @param {!WebInspector.DOMNode} node
* @param {function(?WebInspector.CSSStyleSheetHeader)} callback
*/
requestViaInspectorStylesheet: function(node, callback)
{
var frameId = node.frameId() || this.target().resourceTreeModel.mainFrame.id;
var headers = this._styleSheetIdToHeader.values();
for (var i = 0; i < headers.length; ++i) {
var styleSheetHeader = headers[i];
if (styleSheetHeader.frameId === frameId && styleSheetHeader.isViaInspector()) {
callback(styleSheetHeader);
return;
}
}
/**
* @this {WebInspector.CSSStyleModel}
* @param {?Protocol.Error} error
* @param {!CSSAgent.StyleSheetId} styleSheetId
*/
function innerCallback(error, styleSheetId)
{
if (error) {
console.error(error);
callback(null);
}
callback(this._styleSheetIdToHeader.get(styleSheetId) || null);
}
this._agent.createStyleSheet(frameId, innerCallback.bind(this));
},
mediaQueryResultChanged: function()
{
this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged);
},
/**
* @param {!CSSAgent.StyleSheetId} id
* @return {?WebInspector.CSSStyleSheetHeader}
*/
styleSheetHeaderForId: function(id)
{
return this._styleSheetIdToHeader.get(id) || null;
},
/**
* @return {!Array.<!WebInspector.CSSStyleSheetHeader>}
*/
styleSheetHeaders: function()
{
return this._styleSheetIdToHeader.values();
},
/**
* @param {!DOMAgent.NodeId} nodeId
* @return {?DOMAgent.NodeId}
*/
_ownerDocumentId: function(nodeId)
{
var node = this._domModel.nodeForId(nodeId);
if (!node)
return null;
return node.ownerDocument ? node.ownerDocument.id : null;
},
/**
* @param {!CSSAgent.StyleSheetId} styleSheetId
*/
_fireStyleSheetChanged: function(styleSheetId)
{
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.get(header.styleSheetId));
var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(this, header);
this._styleSheetIdToHeader.put(header.styleSheetId, styleSheetHeader);
var url = styleSheetHeader.resourceURL();
if (!this._styleSheetIdsForURL.get(url))
this._styleSheetIdsForURL.put(url, {});
var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(url);
var styleSheetIds = frameIdToStyleSheetIds[styleSheetHeader.frameId];
if (!styleSheetIds) {
styleSheetIds = [];
frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds;
}
styleSheetIds.push(styleSheetHeader.id);
this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetAdded, styleSheetHeader);
},
/**
* @param {!CSSAgent.StyleSheetId} id
*/
_styleSheetRemoved: function(id)
{
var header = this._styleSheetIdToHeader.get(id);
console.assert(header);
if (!header)
return;
this._styleSheetIdToHeader.remove(id);
var url = header.resourceURL();
var frameIdToStyleSheetIds = /** @type {!Object.<!PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>} */ (this._styleSheetIdsForURL.get(url));
console.assert(frameIdToStyleSheetIds, "No frameId to styleSheetId map is available for given style sheet URL.");
frameIdToStyleSheetIds[header.frameId].remove(id);
if (!frameIdToStyleSheetIds[header.frameId].length) {
delete frameIdToStyleSheetIds[header.frameId];
if (!Object.keys(frameIdToStyleSheetIds).length)
this._styleSheetIdsForURL.remove(url);
}
this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, header);
},
/**
* @param {string} url
* @return {!Array.<!CSSAgent.StyleSheetId>}
*/
styleSheetIdsForURL: function(url)
{
var frameIdToStyleSheetIds = this._styleSheetIdsForURL.get(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.get(url);
if (!styleSheetIdsForFrame)
return {};
return styleSheetIdsForFrame;
},
/**
* @param {!CSSAgent.StyleSheetId} styleSheetId
* @param {string} newText
* @param {boolean} majorChange
* @param {function(?Protocol.Error)} userCallback
*/
setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
{
var header = this._styleSheetIdToHeader.get(styleSheetId);
console.assert(header);
this._pendingCommandsMajorState.push(majorChange);
header.setContent(newText, callback.bind(this));
/**
* @param {?Protocol.Error} error
* @this {WebInspector.CSSStyleModel}
*/
function callback(error)
{
this._pendingCommandsMajorState.pop();
if (!error && majorChange)
this._domModel.markUndoableState();
if (!error && userCallback)
userCallback(error);
}
},
_undoRedoRequested: function()
{
this._pendingCommandsMajorState.push(true);
},
_undoRedoCompleted: function()
{
this._pendingCommandsMajorState.pop();
},
_mainFrameNavigated: function()
{
this._resetStyleSheets();
},
_resetStyleSheets: function()
{
var headers = this._styleSheetIdToHeader.values();
this._styleSheetIdsForURL.clear();
this._styleSheetIdToHeader.clear();
for (var i = 0; i < headers.length; ++i)
this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, headers[i]);
},
__proto__: WebInspector.SDKModel.prototype
}
/**
* @constructor
* @extends {WebInspector.SDKObject}
* @param {!WebInspector.Target} target
* @param {?CSSAgent.StyleSheetId} styleSheetId
* @param {string} url
* @param {number} lineNumber
* @param {number=} columnNumber
*/
WebInspector.CSSLocation = function(target, styleSheetId, url, lineNumber, columnNumber)
{
WebInspector.SDKObject.call(this, target);
this.styleSheetId = styleSheetId;
this.url = url;
this.lineNumber = lineNumber;
this.columnNumber = columnNumber || 0;
}
WebInspector.CSSLocation.prototype = {
__proto__: WebInspector.SDKObject.prototype
}
/**
* @constructor
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSStyle} payload
*/
WebInspector.CSSStyleDeclaration = function(cssModel, payload)
{
this._cssModel = cssModel;
this.styleSheetId = payload.styleSheetId;
this.range = payload.range ? WebInspector.TextRange.fromObject(payload.range) : null;
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;
for (var i = 0; i < payloadPropertyCount; ++i) {
var property = WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]);
this._allProperties.push(property);
}
this._computeActiveProperties();
var propertyIndex = 0;
for (var i = 0; i < this._allProperties.length; ++i) {
var property = this._allProperties[i];
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 {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSStyle} payload
* @return {!WebInspector.CSSStyleDeclaration}
*/
WebInspector.CSSStyleDeclaration.parsePayload = function(cssModel, payload)
{
return new WebInspector.CSSStyleDeclaration(cssModel, payload);
}
/**
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} payload
* @return {!WebInspector.CSSStyleDeclaration}
*/
WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(cssModel, payload)
{
var newPayload = /** @type {!CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" });
if (payload)
newPayload.cssProperties = /** @type {!Array.<!CSSAgent.CSSProperty>} */ (payload);
return new WebInspector.CSSStyleDeclaration(cssModel, newPayload);
}
WebInspector.CSSStyleDeclaration.prototype = {
/**
* @return {!WebInspector.Target}
*/
target: function()
{
return this._cssModel.target();
},
/**
* @param {string} styleSheetId
* @param {!WebInspector.TextRange} oldRange
* @param {!WebInspector.TextRange} newRange
*/
sourceStyleSheetEdited: function(styleSheetId, oldRange, newRange)
{
if (this.styleSheetId !== styleSheetId)
return;
if (this.range)
this.range = this.range.rebaseAfterTextEdit(oldRange, newRange);
for (var i = 0; i < this._allProperties.length; ++i)
this._allProperties[i].sourceStyleSheetEdited(styleSheetId, oldRange, newRange);
},
_computeActiveProperties: function()
{
var activeProperties = {};
for (var i = this._allProperties.length - 1; i >= 0; --i) {
var property = this._allProperties[i];
if (property.styleBased || property.disabled)
continue;
property._setActive(false);
if (!property.parsedOk)
continue;
var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name);
var activeProperty = activeProperties[canonicalName];
if (!activeProperty || (!activeProperty.important && property.important))
activeProperties[canonicalName] = property;
}
for (var propertyName in activeProperties) {
var property = activeProperties[propertyName];
property._setActive(true);
}
},
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 {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) {
if (this.allProperties[i].range)
return i + 1;
}
return 0;
},
/**
* @param {number} index
* @return {!WebInspector.TextRange}
*/
_insertionRange: function(index)
{
var property = this.propertyAt(index);
return property && property.range ? property.range.collapseToStart() : this.range.collapseToEnd();
},
/**
* @param {number=} index
* @return {!WebInspector.CSSProperty}
*/
newBlankProperty: function(index)
{
index = (typeof index === "undefined") ? this.pastLastSourcePropertyIndex() : index;
var property = new WebInspector.CSSProperty(this, index, "", "", false, false, true, false, "", this._insertionRange(index));
property._setActive(true);
return property;
},
/**
* @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
* @this {!WebInspector.CSSStyleDeclaration}
*/
function callback(error, payload)
{
this._cssModel._pendingCommandsMajorState.pop();
if (!userCallback)
return;
if (error) {
console.error(error);
userCallback(null);
} else
userCallback(WebInspector.CSSStyleDeclaration.parsePayload(this._cssModel, payload));
}
if (!this.styleSheetId)
throw "No stylesheet id";
this._cssModel._pendingCommandsMajorState.push(true);
this._cssModel._agent.setPropertyText(this.styleSheetId, this._insertionRange(index), name + ": " + value + ";", 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);
}
}
/**
* @constructor
* @param {!CSSAgent.Selector} payload
*/
WebInspector.CSSRuleSelector = function(payload)
{
this.value = payload.value;
if (payload.range)
this.range = WebInspector.TextRange.fromObject(payload.range);
}
/**
* @param {!CSSAgent.Selector} payload
* @return {!WebInspector.CSSRuleSelector}
*/
WebInspector.CSSRuleSelector.parsePayload = function(payload)
{
return new WebInspector.CSSRuleSelector(payload)
}
WebInspector.CSSRuleSelector.prototype = {
/**
* @param {!WebInspector.TextRange} oldRange
* @param {!WebInspector.TextRange} newRange
*/
sourceStyleRuleEdited: function(oldRange, newRange)
{
if (!this.range)
return;
this.range = this.range.rebaseAfterTextEdit(oldRange, newRange);
}
}
/**
* @constructor
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSRule} payload
* @param {!Array.<number>=} matchingSelectors
*/
WebInspector.CSSRule = function(cssModel, payload, matchingSelectors)
{
this._cssModel = cssModel;
this.styleSheetId = payload.styleSheetId;
if (matchingSelectors)
this.matchingSelectors = matchingSelectors;
/** @type {!Array.<!WebInspector.CSSRuleSelector>} */
this.selectors = [];
for (var i = 0; i < payload.selectorList.selectors.length; ++i) {
var selectorPayload = payload.selectorList.selectors[i];
this.selectors.push(WebInspector.CSSRuleSelector.parsePayload(selectorPayload));
}
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);
}
if (this.styleSheetId) {
var styleSheetHeader = cssModel.styleSheetHeaderForId(this.styleSheetId);
this.sourceURL = styleSheetHeader.sourceURL;
}
this.origin = payload.origin;
this.style = WebInspector.CSSStyleDeclaration.parsePayload(this._cssModel, payload.style);
this.style.parentRule = this;
if (payload.media)
this.media = WebInspector.CSSMedia.parseMediaArrayPayload(cssModel, payload.media);
this._setFrameId();
}
/**
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSRule} payload
* @param {!Array.<number>=} matchingIndices
* @return {!WebInspector.CSSRule}
*/
WebInspector.CSSRule.parsePayload = function(cssModel, payload, matchingIndices)
{
return new WebInspector.CSSRule(cssModel, payload, matchingIndices);
}
WebInspector.CSSRule.prototype = {
/**
* @param {string} styleSheetId
* @param {!WebInspector.TextRange} oldRange
* @param {!WebInspector.TextRange} newRange
*/
sourceStyleSheetEdited: function(styleSheetId, oldRange, newRange)
{
if (this.styleSheetId === styleSheetId) {
if (this.selectorRange)
this.selectorRange = this.selectorRange.rebaseAfterTextEdit(oldRange, newRange);
for (var i = 0; i < this.selectors.length; ++i)
this.selectors[i].sourceStyleRuleEdited(oldRange, newRange);
}
if (this.media) {
for (var i = 0; i < this.media.length; ++i)
this.media[i].sourceStyleSheetEdited(styleSheetId, oldRange, newRange);
}
this.style.sourceStyleSheetEdited(styleSheetId, oldRange, newRange);
},
_setFrameId: function()
{
if (!this.styleSheetId)
return;
var styleSheetHeader = this._cssModel.styleSheetHeaderForId(this.styleSheetId);
this.frameId = styleSheetHeader.frameId;
},
/**
* @return {string}
*/
resourceURL: function()
{
if (!this.styleSheetId)
return "";
var styleSheetHeader = this._cssModel.styleSheetHeaderForId(this.styleSheetId);
return styleSheetHeader.resourceURL();
},
/**
* @param {number} selectorIndex
* @return {number}
*/
lineNumberInSource: function(selectorIndex)
{
var selector = this.selectors[selectorIndex];
if (!selector || !selector.range || !this.styleSheetId)
return 0;
var styleSheetHeader = this._cssModel.styleSheetHeaderForId(this.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 || !this.styleSheetId)
return undefined;
var styleSheetHeader = this._cssModel.styleSheetHeaderForId(this.styleSheetId);
console.assert(styleSheetHeader);
return styleSheetHeader.columnNumberInSource(selector.range.startLine, selector.range.startColumn);
},
/**
* @param {number} index
* @return {?WebInspector.CSSLocation}
*/
rawSelectorLocation: function(index)
{
var lineNumber = this.lineNumberInSource(index);
var columnNumber = this.columnNumberInSource(index);
return new WebInspector.CSSLocation(this._cssModel.target(), this.styleSheetId || null, this.resourceURL(), lineNumber, columnNumber);
},
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 {boolean} important
* @param {boolean} disabled
* @param {boolean} parsedOk
* @param {boolean} implicit
* @param {?string=} text
* @param {!CSSAgent.SourceRange=} range
*/
WebInspector.CSSProperty = function(ownerStyle, index, name, value, important, disabled, parsedOk, implicit, text, range)
{
this.ownerStyle = ownerStyle;
this.index = index;
this.name = name;
this.value = value;
this.important = important;
this.disabled = disabled;
this.parsedOk = parsedOk;
this.implicit = implicit;
this.text = text;
this.range = range ? WebInspector.TextRange.fromObject(range) : null;
}
/**
* @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:
// important: false
// parsedOk: true
// implicit: false
// disabled: false
var result = new WebInspector.CSSProperty(
ownerStyle, index, payload.name, payload.value, payload.important || false, payload.disabled || false, ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range);
return result;
}
WebInspector.CSSProperty.prototype = {
/**
* @param {string} styleSheetId
* @param {!WebInspector.TextRange} oldRange
* @param {!WebInspector.TextRange} newRange
*/
sourceStyleSheetEdited: function(styleSheetId, oldRange, newRange)
{
if (this.ownerStyle.styleSheetId !== styleSheetId)
return;
if (this.range)
this.range = this.range.rebaseAfterTextEdit(oldRange, newRange);
},
/**
* @param {boolean} active
*/
_setActive: function(active)
{
this._active = active;
},
get propertyText()
{
if (this.text !== undefined)
return this.text;
if (this.name === "")
return "";
return this.name + ": " + this.value + (this.important ? " !important" : "") + ";";
},
get isLive()
{
return this.active || this.styleBased;
},
get active()
{
return typeof this._active === "boolean" && this._active;
},
get styleBased()
{
return !this.range;
},
get inactive()
{
return typeof this._active === "boolean" && !this._active;
},
/**
* @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
* @this {WebInspector.CSSProperty}
*/
function callback(error, stylePayload)
{
this.ownerStyle._cssModel._pendingCommandsMajorState.pop();
if (!error) {
if (majorChange)
this.ownerStyle._cssModel._domModel.markUndoableState();
var style = WebInspector.CSSStyleDeclaration.parsePayload(this.ownerStyle._cssModel, 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.styleSheetId)
throw "No owner style id";
// An index past all the properties adds a new property to the style.
var cssModel = this.ownerStyle._cssModel;
cssModel._pendingCommandsMajorState.push(majorChange);
var range = /** @type {!WebInspector.TextRange} */ (this.range);
cssModel._agent.setPropertyText(this.ownerStyle.styleSheetId, overwrite ? range : range.collapseToStart(), propertyText, 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.important ? " !important" : "") + ";"
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) {
if (userCallback)
userCallback(this.ownerStyle);
return;
}
if (disabled)
this.setText("/* " + this.text + " */", true, true, userCallback);
else
this.setText(this.text.substring(2, this.text.length - 2).trim(), true, true, userCallback);
}
}
/**
* @constructor
* @param {!CSSAgent.MediaQueryExpression} payload
*/
WebInspector.CSSMediaQueryExpression = function(payload)
{
this._value = payload.value;
this._unit = payload.unit;
this._feature = payload.feature;
this._computedLength = payload.computedLength || null;
}
/**
* @param {!CSSAgent.MediaQueryExpression} payload
* @return {!WebInspector.CSSMediaQueryExpression}
*/
WebInspector.CSSMediaQueryExpression.parsePayload = function(payload)
{
return new WebInspector.CSSMediaQueryExpression(payload);
}
WebInspector.CSSMediaQueryExpression.prototype = {
/**
* @return {number}
*/
value: function()
{
return this._value;
},
/**
* @return {string}
*/
unit: function()
{
return this._unit;
},
/**
* @return {string}
*/
feature: function()
{
return this._feature;
},
/**
* @return {?number}
*/
computedLength: function()
{
return this._computedLength;
}
}
/**
* @constructor
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSMedia} payload
*/
WebInspector.CSSMedia = function(cssModel, payload)
{
this._cssModel = cssModel
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;
this.mediaList = null;
if (payload.mediaList) {
this.mediaList = [];
for (var i = 0; i < payload.mediaList.length; ++i) {
var mediaQueryPayload = payload.mediaList[i];
var mediaQueryExpressions = [];
for (var j = 0; j < mediaQueryPayload.length; ++j)
mediaQueryExpressions.push(WebInspector.CSSMediaQueryExpression.parsePayload(mediaQueryPayload[j]));
this.mediaList.push(mediaQueryExpressions);
}
}
}
WebInspector.CSSMedia.Source = {
LINKED_SHEET: "linkedSheet",
INLINE_SHEET: "inlineSheet",
MEDIA_RULE: "mediaRule",
IMPORT_RULE: "importRule"
};
/**
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSMedia} payload
* @return {!WebInspector.CSSMedia}
*/
WebInspector.CSSMedia.parsePayload = function(cssModel, payload)
{
return new WebInspector.CSSMedia(cssModel, payload);
}
/**
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!Array.<!CSSAgent.CSSMedia>} payload
* @return {!Array.<!WebInspector.CSSMedia>}
*/
WebInspector.CSSMedia.parseMediaArrayPayload = function(cssModel, payload)
{
var result = [];
for (var i = 0; i < payload.length; ++i)
result.push(WebInspector.CSSMedia.parsePayload(cssModel, payload[i]));
return result;
}
WebInspector.CSSMedia.prototype = {
/**
* @param {string} styleSheetId
* @param {!WebInspector.TextRange} oldRange
* @param {!WebInspector.TextRange} newRange
*/
sourceStyleSheetEdited: function(styleSheetId, oldRange, newRange)
{
if (this.parentStyleSheetId !== styleSheetId)
return;
if (this.range)
this.range = this.range.rebaseAfterTextEdit(oldRange, newRange);
},
/**
* @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 ? this._cssModel.styleSheetHeaderForId(this.parentStyleSheetId) : null;
},
/**
* @return {?WebInspector.CSSLocation}
*/
rawLocation: function()
{
if (!this.header() || this.lineNumberInSource() === undefined)
return null;
var lineNumber = Number(this.lineNumberInSource());
return new WebInspector.CSSLocation(this._cssModel.target(), this.header().id, this.sourceURL, lineNumber, this.columnNumberInSource());
}
}
/**
* @constructor
* @implements {WebInspector.ContentProvider}
* @param {!WebInspector.CSSStyleModel} cssModel
* @param {!CSSAgent.CSSStyleSheetHeader} payload
*/
WebInspector.CSSStyleSheetHeader = function(cssModel, payload)
{
this._cssModel = cssModel;
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;
}
WebInspector.CSSStyleSheetHeader.prototype = {
/**
* @return {!WebInspector.Target}
*/
target: function()
{
return this._cssModel.target();
},
/**
* @return {string}
*/
resourceURL: function()
{
return this.isViaInspector() ? this._viaInspectorResourceURL() : this.sourceURL;
},
/**
* @return {string}
*/
_viaInspectorResourceURL: function()
{
var frame = this._cssModel.target().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
* @return {string}
*/
contentURL: function()
{
return this.resourceURL();
},
/**
* @override
* @return {!WebInspector.ResourceType}
*/
contentType: function()
{
return WebInspector.resourceTypes.Stylesheet;
},
/**
* @param {string} text
* @return {string}
*/
_trimSourceURL: function(text)
{
var sourceURLRegex = /\n[\040\t]*\/\*[#@][\040\t]sourceURL=[\040\t]*([^\s]*)[\040\t]*\*\/[\040\t]*$/mg;
return text.replace(sourceURLRegex, "");
},
/**
* @override
* @param {function(string)} callback
*/
requestContent: function(callback)
{
this._cssModel._agent.getStyleSheetText(this.id, textCallback.bind(this));
/**
* @this {WebInspector.CSSStyleSheetHeader}
*/
function textCallback(error, text)
{
if (error) {
WebInspector.console.error("Failed to get text for stylesheet " + this.id + ": " + error);
text = "";
// Fall through.
}
text = this._trimSourceURL(text);
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);
},
/**
* @param {string} newText
* @param {function(?Protocol.Error)} callback
*/
setContent: function(newText, callback)
{
newText = this._trimSourceURL(newText);
if (this.hasSourceURL)
newText += "\n/*# sourceURL=" + this.sourceURL + " */";
this._cssModel._agent.setStyleSheetText(this.id, newText, callback);
},
/**
* @return {boolean}
*/
isViaInspector: function()
{
return this.origin === "inspector";
}
}
/**
* @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);
},
}
/**
* @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 = {
/**
* @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];
this._cssModel._agent.getComputedStyleForNode(nodeId, resultCallback.bind(this, nodeId));
/**
* @param {!DOMAgent.NodeId} nodeId
* @param {?Protocol.Error} error
* @param {!Array.<!CSSAgent.CSSComputedStyleProperty>} computedPayload
* @this {WebInspector.CSSStyleModel.ComputedStyleLoader}
*/
function resultCallback(nodeId, error, computedPayload)
{
var computedStyle = (error || !computedPayload) ? null : WebInspector.CSSStyleDeclaration.parseComputedStylePayload(this._cssModel, 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;