2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.Object}
35 WebInspector.CSSStyleModel = function()
37 new WebInspector.CSSStyleModelResourceBinding(this);
40 WebInspector.CSSStyleModel.parseRuleArrayPayload = function(ruleArray)
43 for (var i = 0; i < ruleArray.length; ++i)
44 result.push(WebInspector.CSSRule.parsePayload(ruleArray[i]));
48 WebInspector.CSSStyleModel.Events = {
52 WebInspector.CSSStyleModel.prototype = {
53 getStylesAsync: function(nodeId, forcedPseudoClasses, userCallback)
55 function callback(userCallback, error, payload)
64 if ("inlineStyle" in payload)
65 result.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(payload.inlineStyle);
67 result.computedStyle = WebInspector.CSSStyleDeclaration.parsePayload(payload.computedStyle);
68 result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleArrayPayload(payload.matchedCSSRules);
70 result.styleAttributes = {};
71 var payloadStyleAttributes = payload.styleAttributes;
72 for (var i = 0; i < payloadStyleAttributes.length; ++i) {
73 var name = payloadStyleAttributes[i].name;
74 result.styleAttributes[name] = WebInspector.CSSStyleDeclaration.parsePayload(payloadStyleAttributes[i].style);
77 result.pseudoElements = [];
78 for (var i = 0; i < payload.pseudoElements.length; ++i) {
79 var entryPayload = payload.pseudoElements[i];
80 result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleArrayPayload(entryPayload.rules) });
83 result.inherited = [];
84 for (var i = 0; i < payload.inherited.length; ++i) {
85 var entryPayload = payload.inherited[i];
87 if ("inlineStyle" in entryPayload)
88 entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle);
89 if ("matchedCSSRules" in entryPayload)
90 entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleArrayPayload(entryPayload.matchedCSSRules);
91 result.inherited.push(entry);
98 CSSAgent.getStylesForNode(nodeId, forcedPseudoClasses || [], callback.bind(null, userCallback));
101 getComputedStyleAsync: function(nodeId, userCallback)
103 function callback(userCallback, error, stylePayload)
108 userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload));
111 CSSAgent.getComputedStyleForNode(nodeId, callback.bind(null, userCallback));
114 getInlineStyleAsync: function(nodeId, userCallback)
116 function callback(userCallback, error, stylePayload)
121 userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload));
124 CSSAgent.getInlineStyleForNode(nodeId, callback.bind(null, userCallback));
127 setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback)
129 function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
131 if (!selectedNodeIds)
133 var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
134 var rule = WebInspector.CSSRule.parsePayload(rulePayload);
135 successCallback(rule, doesAffectSelectedNode);
136 this._fireStyleSheetChanged(rule.id.styleSheetId, true);
139 function callback(nodeId, successCallback, failureCallback, error, newSelector, rulePayload)
141 // FIXME: looks like rulePayload is always null.
145 var documentElementId = this._documentElementId(nodeId);
146 if (documentElementId)
147 WebInspector.domAgent.querySelectorAll(documentElementId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
153 CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback, newSelector));
156 addRule: function(nodeId, selector, successCallback, failureCallback)
158 function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
160 if (!selectedNodeIds)
163 var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
164 var rule = WebInspector.CSSRule.parsePayload(rulePayload);
165 successCallback(rule, doesAffectSelectedNode);
166 this._fireStyleSheetChanged(rule.id.styleSheetId, true);
169 function callback(successCallback, failureCallback, selector, error, rulePayload)
172 // Invalid syntax for a selector
175 var documentElementId = this._documentElementId(nodeId);
176 if (documentElementId)
177 WebInspector.domAgent.querySelectorAll(documentElementId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
183 CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector));
186 _documentElementId: function(nodeId)
188 var node = WebInspector.domAgent.nodeForId(nodeId);
191 return node.ownerDocumentElement().id;
194 _fireStyleSheetChanged: function(styleSheetId, majorChange, callback)
196 callback = callback || function() {};
198 if (!majorChange || !styleSheetId || !this.hasEventListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged)) {
203 function mycallback(error, content)
206 this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, content: content, majorChange: majorChange });
210 CSSAgent.getStyleSheetText(styleSheetId, mycallback.bind(this));
213 setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
215 function callback(error)
218 this._fireStyleSheetChanged(styleSheetId, majorChange, userCallback ? userCallback.bind(this, error) : null);
220 CSSAgent.setStyleSheetText(styleSheetId, newText, callback.bind(this));
224 WebInspector.CSSStyleModel.prototype.__proto__ = WebInspector.Object.prototype;
230 WebInspector.CSSStyleDeclaration = function(payload)
232 this.id = payload.styleId;
233 this.width = payload.width;
234 this.height = payload.height;
235 this.range = payload.range;
236 this._shorthandValues = WebInspector.CSSStyleDeclaration.buildShorthandValueMap(payload.shorthandEntries);
237 this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty }
238 this._allProperties = []; // ALL properties: [ CSSProperty ]
239 this._longhandProperties = {}; // shorthandName -> [ CSSProperty ]
240 this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty }
241 var payloadPropertyCount = payload.cssProperties.length;
243 var propertyIndex = 0;
244 for (var i = 0; i < payloadPropertyCount; ++i) {
245 var property = new WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]);
246 this._allProperties.push(property);
247 if (property.disabled)
248 this.__disabledProperties[i] = property;
249 if (!property.active && !property.styleBased)
251 var name = property.name;
252 this[propertyIndex] = name;
253 this._livePropertyMap[name] = property;
255 // Index longhand properties.
256 if (property.shorthand) { // only for parsed
257 var longhands = this._longhandProperties[property.shorthand];
260 this._longhandProperties[property.shorthand] = longhands;
262 longhands.push(property);
266 this.length = propertyIndex;
267 if ("cssText" in payload)
268 this.cssText = payload.cssText;
271 WebInspector.CSSStyleDeclaration.buildShorthandValueMap = function(shorthandEntries)
274 for (var i = 0; i < shorthandEntries.length; ++i)
275 result[shorthandEntries[i].name] = shorthandEntries[i].value;
279 WebInspector.CSSStyleDeclaration.parsePayload = function(payload)
281 return new WebInspector.CSSStyleDeclaration(payload);
284 WebInspector.CSSStyleDeclaration.prototype = {
287 return this._allProperties;
290 getLiveProperty: function(name)
292 return this._livePropertyMap[name];
295 getPropertyValue: function(name)
297 var property = this._livePropertyMap[name];
298 return property ? property.value : "";
301 getPropertyPriority: function(name)
303 var property = this._livePropertyMap[name];
304 return property ? property.priority : "";
307 getPropertyShorthand: function(name)
309 var property = this._livePropertyMap[name];
310 return property ? property.shorthand : "";
313 isPropertyImplicit: function(name)
315 var property = this._livePropertyMap[name];
316 return property ? property.implicit : "";
319 styleTextWithShorthands: function()
322 var foundProperties = {};
323 for (var i = 0; i < this.length; ++i) {
324 var individualProperty = this[i];
325 var shorthandProperty = this.getPropertyShorthand(individualProperty);
326 var propertyName = (shorthandProperty || individualProperty);
328 if (propertyName in foundProperties)
331 if (shorthandProperty) {
332 var value = this.getShorthandValue(shorthandProperty);
333 var priority = this.getShorthandPriority(shorthandProperty);
335 var value = this.getPropertyValue(individualProperty);
336 var priority = this.getPropertyPriority(individualProperty);
339 foundProperties[propertyName] = true;
341 cssText += propertyName + ": " + value;
343 cssText += " !" + priority;
350 getLonghandProperties: function(name)
352 return this._longhandProperties[name] || [];
355 getShorthandValue: function(shorthandProperty)
357 var property = this.getLiveProperty(shorthandProperty);
358 return property ? property.value : this._shorthandValues[shorthandProperty];
361 getShorthandPriority: function(shorthandProperty)
363 var priority = this.getPropertyPriority(shorthandProperty);
367 var longhands = this._longhandProperties[shorthandProperty];
368 return longhands ? this.getPropertyPriority(longhands[0]) : null;
371 propertyAt: function(index)
373 return (index < this.allProperties.length) ? this.allProperties[index] : null;
376 pastLastSourcePropertyIndex: function()
378 for (var i = this.allProperties.length - 1; i >= 0; --i) {
379 var property = this.allProperties[i];
380 if (property.active || property.disabled)
386 newBlankProperty: function()
388 return new WebInspector.CSSProperty(this, this.pastLastSourcePropertyIndex(), "", "", "", "active", true, false, false, "");
391 insertPropertyAt: function(index, name, value, userCallback)
393 function callback(userCallback, error, payload)
399 console.error(JSON.stringify(error));
402 userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
403 WebInspector.cssModel._fireStyleSheetChanged(this.id.styleSheetId, true);
407 CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(null, userCallback));
410 appendProperty: function(name, value, userCallback)
412 this.insertPropertyAt(this.allProperties.length, name, value, userCallback);
419 WebInspector.CSSRule = function(payload)
421 this.id = payload.ruleId;
422 this.selectorText = payload.selectorText;
423 this.sourceLine = payload.sourceLine;
424 this.sourceURL = payload.sourceURL;
425 this.origin = payload.origin;
426 this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style);
427 this.style.parentRule = this;
428 this.selectorRange = payload.selectorRange;
431 WebInspector.CSSRule.parsePayload = function(payload)
433 return new WebInspector.CSSRule(payload);
436 WebInspector.CSSRule.prototype = {
439 return this.origin === "user-agent";
444 return this.origin === "user";
449 return this.origin === "inspector";
454 return this.origin === "";
461 WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, shorthand, text)
463 this.ownerStyle = ownerStyle;
467 this.priority = priority;
468 this.status = status;
469 this.parsedOk = parsedOk;
470 this.implicit = implicit;
471 this.shorthand = shorthand;
475 WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload)
477 // The following default field values are used in the payload:
483 var result = new WebInspector.CSSProperty(
484 ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? payload.parsedOk : true, !!payload.implicit, payload.shorthandName || "", payload.text);
488 WebInspector.CSSProperty.prototype = {
491 if (this.text !== undefined)
494 if (this.name === "")
496 return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";";
501 return this.active || this.styleBased;
506 return this.status === "active";
511 return this.status === "style";
516 return this.status === "inactive";
521 return this.status === "disabled";
524 // Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText.
525 setText: function(propertyText, majorChange, userCallback)
527 function enabledCallback(style)
530 WebInspector.cssModel._fireStyleSheetChanged(style.id.styleSheetId, majorChange, userCallback ? userCallback.bind(null, style) : null);
531 else if (userCallback)
535 function callback(error, stylePayload)
538 this.text = propertyText;
539 var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
540 var newProperty = style.allProperties[this.index];
542 if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) {
543 newProperty.setDisabled(false, enabledCallback);
547 WebInspector.cssModel._fireStyleSheetChanged(style.id.styleSheetId, majorChange, userCallback ? userCallback.bind(null, style) : null);
554 if (!this.ownerStyle)
555 throw "No ownerStyle for property";
557 // An index past all the properties adds a new property to the style.
558 CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, this.index < this.ownerStyle.pastLastSourcePropertyIndex(), callback.bind(this));
561 setValue: function(newValue, majorChange, userCallback)
563 var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";"
564 this.setText(text, majorChange, userCallback);
567 setDisabled: function(disabled, userCallback)
569 if (!this.ownerStyle && userCallback)
571 if (disabled === this.disabled && userCallback)
572 userCallback(this.ownerStyle);
574 function callback(error, stylePayload)
582 var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
585 WebInspector.cssModel._fireStyleSheetChanged(this.ownerStyle.id.styleSheetId, false);
588 CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this));
595 WebInspector.CSSStyleSheet = function(payload)
597 this.id = payload.styleSheetId;
600 for (var i = 0; i < payload.rules.length; ++i) {
601 var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]);
602 this.rules.push(rule);
604 this.styles[rule.style.id] = rule.style;
606 if ("text" in payload)
607 this._text = payload.text;
610 WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback)
612 function callback(error, styleSheetPayload)
617 userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload));
619 CSSAgent.getStyleSheet(styleSheetId, callback.bind(this));
622 WebInspector.CSSStyleSheet.prototype = {
628 setText: function(newText, majorChange, userCallback)
630 function callback(error)
635 WebInspector.cssModel._fireStyleSheetChanged(this.id, majorChange);
638 CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this));
644 * @implements {WebInspector.ResourceDomainModelBinding}
646 WebInspector.CSSStyleModelResourceBinding = function(cssModel)
648 this._cssModel = cssModel;
649 this._urlToStyleSheetId = {};
650 this._styleSheetIdToURL = {};
651 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
652 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
653 WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Stylesheet, this);
656 WebInspector.CSSStyleModelResourceBinding.prototype = {
657 setContent: function(resource, content, majorChange, userCallback)
659 if (this._urlToStyleSheetId[resource.url]) {
660 this._innerSetContent(resource.url, content, majorChange, userCallback);
663 this._loadStyleSheetHeaders(this._innerSetContent.bind(this, resource.url, content, majorChange, userCallback));
666 _inspectedURLChanged: function(event)
668 // Main frame navigation - clear history.
669 this._urlToStyleSheetId = {};
670 this._styleSheetIdToURL = {};
673 _innerSetContent: function(url, content, majorChange, userCallback, error)
680 var styleSheetId = this._urlToStyleSheetId[url];
683 userCallback("No stylesheet found: " + url);
686 this._cssModel.setStyleSheetText(styleSheetId, content, majorChange, userCallback);
689 _loadStyleSheetHeaders: function(callback)
691 function didGetAllStyleSheets(error, infos)
698 for (var i = 0; i < infos.length; ++i) {
700 this._urlToStyleSheetId[info.sourceURL] = info.styleSheetId;
701 this._styleSheetIdToURL[info.styleSheetId] = info.sourceURL;
705 CSSAgent.getAllStyleSheets(didGetAllStyleSheets.bind(this));
708 _styleSheetChanged: function(event)
710 var styleSheetId = event.data.styleSheetId;
711 function setContent()
713 var url = this._styleSheetIdToURL[styleSheetId];
717 var resource = WebInspector.resourceForURL(url);
721 var majorChange = event.data.majorChange;
723 resource.addRevision(event.data.content);
726 if (!this._styleSheetIdToURL[styleSheetId]) {
727 this._loadStyleSheetHeaders(setContent.bind(this));
730 setContent.call(this);
734 WebInspector.CSSStyleModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;