summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js1812
1 files changed, 1812 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js
new file mode 100644
index 00000000000..c63a310d7a4
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js
@@ -0,0 +1,1812 @@
+/*
+ * 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.TargetAwareObject}
+ * @param {!WebInspector.Target} target
+ */
+WebInspector.CSSStyleModel = function(target)
+{
+ WebInspector.TargetAwareObject.call(this, 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));
+ this._resetStyleSheets();
+}
+
+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 = {
+ /**
+ * @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} 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
+ * @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, needPseudo, needInherited, 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 = Object.values(this._styleSheetIdToHeader);
+ /**
+ * @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} selector
+ * @param {function(!WebInspector.CSSRule)} successCallback
+ * @param {function()} failureCallback
+ */
+ addRule: function(styleSheetId, node, selector, successCallback, failureCallback)
+ {
+ this._pendingCommandsMajorState.push(true);
+ this._agent.addRule(styleSheetId, selector, 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;
+ for (var styleSheetId in this._styleSheetIdToHeader) {
+ var styleSheetHeader = this._styleSheetIdToHeader[styleSheetId];
+ 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[styleSheetId]);
+ }
+
+ 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[id];
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.CSSStyleSheetHeader>}
+ */
+ styleSheetHeaders: function()
+ {
+ return Object.values(this._styleSheetIdToHeader);
+ },
+
+ /**
+ * @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[header.styleSheetId]);
+ var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(this, 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.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetAdded, styleSheetHeader);
+ },
+
+ /**
+ * @param {!CSSAgent.StyleSheetId} id
+ */
+ _styleSheetRemoved: function(id)
+ {
+ var header = this._styleSheetIdToHeader[id];
+ console.assert(header);
+ if (!header)
+ return;
+ 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.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.StyleSheetId} styleSheetId
+ * @param {string} newText
+ * @param {boolean} majorChange
+ * @param {function(?Protocol.Error)} userCallback
+ */
+ setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
+ {
+ var header = this._styleSheetIdToHeader[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()
+ {
+ /** @type {!Object.<string, !Object.<!PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>>} */
+ this._styleSheetIdsForURL = {};
+ /** @type {!Object.<!CSSAgent.StyleSheetId, !WebInspector.CSSStyleSheetHeader>} */
+ this._styleSheetIdToHeader = {};
+ },
+
+ 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.TargetAwareObject.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 uiSourceCode.uiLocation(cssLocation.lineNumber, cssLocation.columnNumber);
+ },
+
+ dispose: function()
+ {
+ WebInspector.LiveLocation.prototype.dispose.call(this);
+ if (this._header)
+ this._header._removeLocation(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 {!WebInspector.Target} target
+ * @param {string} url
+ * @param {number} lineNumber
+ * @param {number=} columnNumber
+ */
+WebInspector.CSSLocation = function(target, url, lineNumber, columnNumber)
+{
+ this._cssModel = target.cssModel;
+ this.url = url;
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber || 0;
+}
+
+WebInspector.CSSLocation.prototype = {
+ /**
+ * @param {?CSSAgent.StyleSheetId} styleSheetId
+ * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
+ * @return {?WebInspector.LiveLocation}
+ */
+ createLiveLocation: function(styleSheetId, updateDelegate)
+ {
+ var header = styleSheetId ? this._cssModel.styleSheetHeaderForId(styleSheetId) : null;
+ return new WebInspector.CSSStyleModel.LiveLocation(this._cssModel, header, this, updateDelegate);
+ },
+
+ /**
+ * @return {?WebInspector.UILocation}
+ */
+ toUILocation: function()
+ {
+ return this._cssModel.rawLocationToUILocation(this);
+ }
+}
+
+/**
+ * @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 = {
+ /**
+ * @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 {!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;
+ this.selectors = payload.selectorList.selectors;
+ for (var i = 0; i < this.selectors.length; ++i) {
+ var selector = this.selectors[i];
+ if (selector.range)
+ selector.range = WebInspector.TextRange.fromObject(selector.range);
+ }
+ 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._setRawLocationAndFrameId();
+}
+
+/**
+ * @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) {
+ var selector = this.selectors[i];
+ if (selector.range)
+ selector.range = selector.range.rebaseAfterTextEdit(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);
+ },
+
+ _setRawLocationAndFrameId: function()
+ {
+ if (!this.styleSheetId)
+ return;
+ var styleSheetHeader = this._cssModel.styleSheetHeaderForId(this.styleSheetId);
+ this.frameId = styleSheetHeader.frameId;
+ var url = styleSheetHeader.resourceURL();
+ if (!url)
+ return;
+ this.rawLocation = new WebInspector.CSSLocation(this._cssModel.target(), url, this.lineNumberInSource(0), this.columnNumberInSource(0));
+ },
+
+ /**
+ * @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);
+ },
+
+ 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);
+ },
+
+ /**
+ * @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(this.ownerStyle._cssModel.target(), url, line, column);
+ return rawLocation.toUILocation();
+ }
+}
+
+/**
+ * @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
+ */
+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;
+ }
+}
+
+/**
+ * @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;
+ /** @type {!Set.<!WebInspector.CSSStyleModel.LiveLocation>} */
+ this._locations = new Set();
+ /** @type {!Array.<!WebInspector.SourceMapping>} */
+ this._sourceMappings = [];
+}
+
+WebInspector.CSSStyleSheetHeader.prototype = {
+ /**
+ * @return {string}
+ */
+ resourceURL: function()
+ {
+ return this.isViaInspector() ? this._viaInspectorResourceURL() : this.sourceURL;
+ },
+
+ /**
+ * @param {!WebInspector.CSSStyleModel.LiveLocation} location
+ */
+ addLiveLocation: function(location)
+ {
+ this._locations.add(location);
+ location.update();
+ },
+
+ updateLocations: function()
+ {
+ var items = this._locations.values();
+ 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 = null;
+ var rawLocation = new WebInspector.CSSLocation(this._cssModel.target(), this.resourceURL(), lineNumber, columnNumber);
+ for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
+ uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
+ return uiLocation;
+ },
+
+ /**
+ * @param {!WebInspector.SourceMapping} sourceMapping
+ */
+ pushSourceMapping: function(sourceMapping)
+ {
+ this._sourceMappings.push(sourceMapping);
+ this.updateLocations();
+ },
+
+ /**
+ * @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.messageSink.addErrorMessage("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;