575 lines
19 KiB
JavaScript
575 lines
19 KiB
JavaScript
/*
|
|
* 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));
|
|
}
|
|
}
|