| /* |
| * 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. |
| */ |
| |
| WebInspector.CSSStyleModel = function() |
| { |
| } |
| |
| WebInspector.CSSStyleModel.parseRuleArrayPayload = function(ruleArray) |
| { |
| var result = []; |
| for (var i = 0; i < ruleArray.length; ++i) |
| result.push(WebInspector.CSSRule.parsePayload(ruleArray[i])); |
| return result; |
| } |
| |
| WebInspector.CSSStyleModel.prototype = { |
| getStylesAsync: function(nodeId, userCallback) |
| { |
| function callback(userCallback, payload) |
| { |
| if (!payload) { |
| if (userCallback) |
| userCallback(null); |
| return; |
| } |
| |
| var result = {}; |
| if ("inlineStyle" in payload) |
| result.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(payload.inlineStyle); |
| |
| result.computedStyle = WebInspector.CSSStyleDeclaration.parsePayload(payload.computedStyle); |
| result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleArrayPayload(payload.matchedCSSRules); |
| |
| result.styleAttributes = {}; |
| for (var name in payload.styleAttributes) |
| result.styleAttributes[name] = WebInspector.CSSStyleDeclaration.parsePayload(payload.styleAttributes[name]); |
| |
| result.pseudoElements = []; |
| for (var i = 0; i < payload.pseudoElements.length; ++i) { |
| var entryPayload = payload.pseudoElements[i]; |
| result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleArrayPayload(entryPayload.rules) }); |
| } |
| |
| result.inherited = []; |
| for (var i = 0; i < payload.inherited.length; ++i) { |
| var entryPayload = payload.inherited[i]; |
| var entry = {}; |
| if ("inlineStyle" in entryPayload) |
| entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle); |
| if ("matchedCSSRules" in entryPayload) |
| entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleArrayPayload(entryPayload.matchedCSSRules); |
| result.inherited.push(entry); |
| } |
| |
| if (userCallback) |
| userCallback(result); |
| } |
| |
| InspectorBackend.getStylesForNode(nodeId, callback.bind(null, userCallback)); |
| }, |
| |
| getComputedStyleAsync: function(nodeId, userCallback) |
| { |
| function callback(userCallback, stylePayload) |
| { |
| if (!stylePayload) |
| userCallback(null); |
| else |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload)); |
| } |
| |
| InspectorBackend.getComputedStyleForNode(nodeId, callback.bind(null, userCallback)); |
| }, |
| |
| getInlineStyleAsync: function(nodeId, userCallback) |
| { |
| function callback(userCallback, stylePayload) |
| { |
| if (!stylePayload) |
| userCallback(null); |
| else |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload)); |
| } |
| |
| InspectorBackend.getInlineStyleForNode(nodeId, callback.bind(null, userCallback)); |
| }, |
| |
| setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback) |
| { |
| function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds) |
| { |
| var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0); |
| var rule = WebInspector.CSSRule.parsePayload(rulePayload); |
| successCallback(rule, doesAffectSelectedNode); |
| this._styleSheetChanged(rule.id.styleSheetId, true); |
| } |
| |
| function callback(nodeId, successCallback, failureCallback, newSelector, rulePayload) |
| { |
| if (!rulePayload) |
| failureCallback(); |
| else |
| InspectorBackend.querySelectorAll(nodeId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); |
| } |
| |
| InspectorBackend.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback)); |
| }, |
| |
| addRule: function(nodeId, selector, successCallback, failureCallback) |
| { |
| function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds) |
| { |
| var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0); |
| var rule = WebInspector.CSSRule.parsePayload(rulePayload); |
| successCallback(rule, doesAffectSelectedNode); |
| this._styleSheetChanged(rule.id.styleSheetId, true); |
| } |
| |
| function callback(successCallback, failureCallback, selector, rulePayload) |
| { |
| if (!rulePayload) { |
| // Invalid syntax for a selector |
| failureCallback(); |
| } else |
| InspectorBackend.querySelectorAll(nodeId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); |
| } |
| |
| InspectorBackend.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector)); |
| }, |
| |
| _styleSheetChanged: function(styleSheetId, majorChange) |
| { |
| if (!majorChange || !styleSheetId) |
| return; |
| |
| function callback(href, content) |
| { |
| var resource = WebInspector.resourceForURL(href); |
| if (resource && resource.type === WebInspector.Resource.Type.Stylesheet) |
| resource.setContent(content, this._onRevert.bind(this, styleSheetId)); |
| } |
| InspectorBackend.getStyleSheetText(styleSheetId, callback.bind(this)); |
| }, |
| |
| _onRevert: function(styleSheetId, contentToRevertTo) |
| { |
| function callback(success) |
| { |
| this._styleSheetChanged(styleSheetId, true); |
| this.dispatchEventToListeners("stylesheet changed"); |
| } |
| InspectorBackend.setStyleSheetText(styleSheetId, contentToRevertTo, callback.bind(this)); |
| } |
| } |
| |
| WebInspector.CSSStyleModel.prototype.__proto__ = WebInspector.Object.prototype; |
| |
| WebInspector.CSSStyleDeclaration = function(payload) |
| { |
| this.id = payload.styleId; |
| this.properties = payload.properties; |
| this._shorthandValues = payload.shorthandValues; |
| this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty } |
| this._allProperties = []; // ALL properties: [ CSSProperty ] |
| this._longhandProperties = {}; // shorthandName -> [ CSSProperty ] |
| this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty } |
| var payloadPropertyCount = payload.cssProperties.length; |
| |
| var propertyIndex = 0; |
| for (var i = 0; i < payloadPropertyCount; ++i) { |
| var property = new WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]); |
| this._allProperties.push(property); |
| if (property.disabled) |
| this.__disabledProperties[i] = property; |
| if (!property.active && !property.styleBased) |
| continue; |
| var name = property.name; |
| this[propertyIndex] = name; |
| this._livePropertyMap[name] = property; |
| |
| // Index longhand properties. |
| if (property.shorthand) { // only for parsed |
| var longhands = this._longhandProperties[property.shorthand]; |
| if (!longhands) { |
| longhands = []; |
| this._longhandProperties[property.shorthand] = longhands; |
| } |
| longhands.push(property); |
| } |
| ++propertyIndex; |
| } |
| this.length = propertyIndex; |
| if ("cssText" in payload) |
| this.cssText = payload.cssText; |
| } |
| |
| WebInspector.CSSStyleDeclaration.parsePayload = function(payload) |
| { |
| return new WebInspector.CSSStyleDeclaration(payload); |
| } |
| |
| WebInspector.CSSStyleDeclaration.prototype = { |
| get allProperties() |
| { |
| return this._allProperties; |
| }, |
| |
| getLiveProperty: function(name) |
| { |
| return this._livePropertyMap[name]; |
| }, |
| |
| getPropertyValue: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.value : ""; |
| }, |
| |
| getPropertyPriority: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.priority : ""; |
| }, |
| |
| getPropertyShorthand: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.shorthand : ""; |
| }, |
| |
| isPropertyImplicit: function(name) |
| { |
| var property = this._livePropertyMap[name]; |
| return property ? property.implicit : ""; |
| }, |
| |
| styleTextWithShorthands: function() |
| { |
| var cssText = ""; |
| var foundProperties = {}; |
| for (var i = 0; i < this.length; ++i) { |
| var individualProperty = this[i]; |
| var shorthandProperty = this.getPropertyShorthand(individualProperty); |
| var propertyName = (shorthandProperty || individualProperty); |
| |
| if (propertyName in foundProperties) |
| continue; |
| |
| if (shorthandProperty) { |
| var value = this.getShorthandValue(shorthandProperty); |
| var priority = this.getShorthandPriority(shorthandProperty); |
| } else { |
| var value = this.getPropertyValue(individualProperty); |
| var priority = this.getPropertyPriority(individualProperty); |
| } |
| |
| foundProperties[propertyName] = true; |
| |
| cssText += propertyName + ": " + value; |
| if (priority) |
| cssText += " !" + priority; |
| cssText += "; "; |
| } |
| |
| return cssText; |
| }, |
| |
| getLonghandProperties: function(name) |
| { |
| return this._longhandProperties[name] || []; |
| }, |
| |
| getShorthandValue: function(shorthandProperty) |
| { |
| var property = this.getLiveProperty(shorthandProperty); |
| return property ? property.value : this._shorthandValues[shorthandProperty]; |
| }, |
| |
| getShorthandPriority: function(shorthandProperty) |
| { |
| var priority = this.getPropertyPriority(shorthandProperty); |
| if (priority) |
| return priority; |
| |
| var longhands = this._longhandProperties[shorthandProperty]; |
| return longhands ? this.getPropertyPriority(longhands[0]) : null; |
| }, |
| |
| propertyAt: function(index) |
| { |
| return (index < this.allProperties.length) ? this.allProperties[index] : null; |
| }, |
| |
| pastLastSourcePropertyIndex: function() |
| { |
| for (var i = this.allProperties.length - 1; i >= 0; --i) { |
| var property = this.allProperties[i]; |
| if (property.active || property.disabled) |
| return i + 1; |
| } |
| return 0; |
| }, |
| |
| newBlankProperty: function() |
| { |
| return new WebInspector.CSSProperty(this, this.pastLastSourcePropertyIndex(), "", "", "", "active", true, false, false, ""); |
| }, |
| |
| insertPropertyAt: function(index, name, value, userCallback) |
| { |
| function callback(userCallback, payload) |
| { |
| if (!userCallback) |
| return; |
| |
| if (!payload) |
| userCallback(null); |
| else { |
| userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload)); |
| WebInspector.cssModel._styleSheetChanged(this.id.styleSheetId, true); |
| } |
| } |
| |
| InspectorBackend.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(null, userCallback)); |
| }, |
| |
| appendProperty: function(name, value, userCallback) |
| { |
| this.insertPropertyAt(this.allProperties.length, name, value, userCallback); |
| } |
| } |
| |
| WebInspector.CSSRule = function(payload) |
| { |
| this.id = payload.ruleId; |
| this.selectorText = payload.selectorText; |
| this.sourceLine = payload.sourceLine; |
| this.sourceURL = payload.sourceURL; |
| this.origin = payload.origin; |
| this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style); |
| this.style.parentRule = this; |
| this.selectorRange = payload.selectorRange; |
| } |
| |
| WebInspector.CSSRule.parsePayload = function(payload) |
| { |
| return new WebInspector.CSSRule(payload); |
| } |
| |
| WebInspector.CSSRule.prototype = { |
| get isUserAgent() |
| { |
| return this.origin === "user-agent"; |
| }, |
| |
| get isUser() |
| { |
| return this.origin === "user"; |
| }, |
| |
| get isViaInspector() |
| { |
| return this.origin === "inspector"; |
| }, |
| |
| get isRegular() |
| { |
| return this.origin === ""; |
| } |
| } |
| |
| WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, shorthand, text) |
| { |
| this.ownerStyle = ownerStyle; |
| this.index = index; |
| this.name = name; |
| this.value = value; |
| this.priority = priority; |
| this.status = status; |
| this.parsedOk = parsedOk; |
| this.implicit = implicit; |
| this.shorthand = shorthand; |
| this.text = text; |
| } |
| |
| WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload) |
| { |
| var result = new WebInspector.CSSProperty( |
| ownerStyle, index, payload.name, payload.value, payload.priority, payload.status, payload.parsedOk, payload.implicit, payload.shorthandName, payload.text); |
| return result; |
| } |
| |
| WebInspector.CSSProperty.prototype = { |
| get propertyText() |
| { |
| if (this.text !== undefined) |
| return this.text; |
| |
| if (this.name === "") |
| return ""; |
| return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";"; |
| }, |
| |
| get isLive() |
| { |
| return this.active || this.styleBased; |
| }, |
| |
| get active() |
| { |
| return this.status === "active"; |
| }, |
| |
| get styleBased() |
| { |
| return this.status === "style"; |
| }, |
| |
| get inactive() |
| { |
| return this.status === "inactive"; |
| }, |
| |
| get disabled() |
| { |
| return this.status === "disabled"; |
| }, |
| |
| // Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText. |
| setText: function(propertyText, majorChange, userCallback) |
| { |
| function enabledCallback(style) |
| { |
| if (style) |
| WebInspector.cssModel._styleSheetChanged(style.id.styleSheetId, majorChange); |
| if (userCallback) |
| userCallback(style); |
| } |
| |
| function callback(stylePayload) |
| { |
| if (stylePayload) { |
| this.text = propertyText; |
| var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload); |
| var newProperty = style.allProperties[this.index]; |
| |
| if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) { |
| newProperty.setDisabled(false, enabledCallback); |
| return; |
| } else |
| WebInspector.cssModel._styleSheetChanged(style.id.styleSheetId, majorChange); |
| if (userCallback) |
| userCallback(style); |
| } else { |
| if (userCallback) |
| userCallback(null); |
| } |
| } |
| |
| if (!this.ownerStyle) |
| throw "No ownerStyle for property"; |
| |
| // An index past all the properties adds a new property to the style. |
| InspectorBackend.setPropertyText(this.ownerStyle.id, this.index, propertyText, this.index < this.ownerStyle.pastLastSourcePropertyIndex(), callback.bind(this)); |
| }, |
| |
| setValue: function(newValue, userCallback) |
| { |
| var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";" |
| this.setText(text, userCallback); |
| }, |
| |
| setDisabled: function(disabled, userCallback) |
| { |
| if (!this.ownerStyle && userCallback) |
| userCallback(null); |
| if (disabled === this.disabled && userCallback) |
| userCallback(this.ownerStyle); |
| |
| function callback(stylePayload) |
| { |
| if (!userCallback) |
| return; |
| if (!stylePayload) |
| userCallback(null); |
| else { |
| var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload); |
| userCallback(style); |
| WebInspector.cssModel._styleSheetChanged(this.ownerStyle.id.styleSheetId, false); |
| } |
| } |
| |
| InspectorBackend.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this)); |
| } |
| } |
| |
| WebInspector.CSSStyleSheet = function(payload) |
| { |
| this.id = payload.styleSheetId; |
| this.sourceURL = payload.sourceURL; |
| this.title = payload.title; |
| this.disabled = payload.disabled; |
| this.rules = []; |
| this.styles = {}; |
| for (var i = 0; i < payload.rules.length; ++i) { |
| var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]); |
| this.rules.push(rule); |
| if (rule.style) |
| this.styles[rule.style.id] = rule.style; |
| } |
| if ("text" in payload) |
| this._text = payload.text; |
| } |
| |
| WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback) |
| { |
| function callback(styleSheetPayload) |
| { |
| if (!styleSheetPayload) |
| userCallback(null); |
| else |
| userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload)); |
| } |
| InspectorBackend.getStyleSheet(styleSheetId, callback.bind(this)); |
| } |
| |
| WebInspector.CSSStyleSheet.prototype = { |
| getText: function() |
| { |
| return this._text; |
| }, |
| |
| setText: function(newText, userCallback) |
| { |
| function callback(styleSheetPayload) |
| { |
| if (!styleSheetPayload) |
| userCallback(null); |
| else { |
| userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload)); |
| WebInspector.cssModel._styleSheetChanged(this.id, true); |
| } |
| } |
| |
| InspectorBackend.setStyleSheetText(this.id, newText, callback.bind(this)); |
| } |
| } |