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
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "InspectorCSSAgent.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "CSSMutableStyleDeclaration.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSPropertySourceData.h"
35 #include "CSSRuleList.h"
36 #include "CSSStyleRule.h"
37 #include "CSSStyleSelector.h"
38 #include "CSSStyleSheet.h"
39 #include "DOMWindow.h"
40 #include "HTMLHeadElement.h"
41 #include "InspectorDOMAgent.h"
42 #include "InspectorValues.h"
45 #include "StyleSheetList.h"
47 #include <wtf/HashSet.h>
48 #include <wtf/Vector.h>
49 #include <wtf/text/CString.h>
51 // Currently implemented model:
56 // priority : <string>, // "" for non-parsedOk properties
57 // implicit : <boolean>,
58 // parsedOk : <boolean>, // whether property is understood by WebCore
59 // status : <string>, // "disabled" | "active" | "inactive" | "style"
60 // shorthandName : <string>,
61 // startOffset : <number>, // Optional - property text start offset in enclosing style declaration. Absent for computed styles and such.
62 // endOffset : <number>, // Optional - property text end offset in enclosing style declaration. Absent for computed styles and such.
65 // name + value + priority : present when the property is enabled
66 // text : present when the property is disabled
68 // For disabled properties, startOffset === endOffset === insertion point for the property.
71 // "disabled" == property disabled by user
72 // "active" == property participates in the computed style calculation
73 // "inactive" == property does no participate in the computed style calculation (i.e. overridden by a subsequent property with the same name)
74 // "style" == property is active and originates from the WebCore CSSStyleDeclaration rather than CSS source code (e.g. implicit longhand properties)
77 // styleId : <string>, // Optional
83 // shorthandValues : {
84 // shorthandName1 : shorthandValue1,
85 // shorthandName2 : shorthandValue2
87 // cssText : <string>, // Optional - declaration text
91 // startOffset, // Optional - for source-based styles only
92 // endOffset, // Optional - for source-based styles only
97 // ruleId : <string>, // Optional
98 // selectorText : <string>,
99 // sourceURL : <string>,
100 // sourceLine : <string>,
101 // origin : <string>, // "" || "user-agent" || "user" || "inspector"
102 // style : #cssStyle,
103 // selectorRange: { start: <number>, end: <number> } // Optional - for source-based rules only
107 // styleSheetId : <number>
108 // sourceURL : <string>
110 // disabled : <boolean>
116 // text : <string> // Optional - whenever the text is available for a text-based stylesheet
122 CSSStyleSheet* InspectorCSSAgent::parentStyleSheet(StyleBase* styleBase)
127 StyleSheet* styleSheet = styleBase->stylesheet();
128 if (styleSheet && styleSheet->isCSSStyleSheet())
129 return static_cast<CSSStyleSheet*>(styleSheet);
135 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(StyleBase* styleBase)
137 if (!styleBase->isStyleRule())
139 CSSRule* rule = static_cast<CSSRule*>(styleBase);
140 if (rule->type() != CSSRule::STYLE_RULE)
142 return static_cast<CSSStyleRule*>(rule);
145 InspectorCSSAgent::InspectorCSSAgent(InspectorDOMAgent* domAgent)
146 : m_domAgent(domAgent)
147 , m_lastStyleSheetId(1)
151 m_domAgent->setDOMListener(this);
154 InspectorCSSAgent::~InspectorCSSAgent()
156 // DOM agent should be destroyed after CSS agent.
157 m_domAgent->setDOMListener(0);
162 void InspectorCSSAgent::reset()
164 m_idToInspectorStyleSheet.clear();
165 m_cssStyleSheetToInspectorStyleSheet.clear();
166 m_nodeToInspectorStyleSheet.clear();
167 m_documentToInspectorStyleSheet.clear();
170 void InspectorCSSAgent::getStylesForNode(ErrorString*, long nodeId, RefPtr<InspectorValue>* result)
172 Element* element = elementForId(nodeId);
176 RefPtr<InspectorObject> resultObject = InspectorObject::create();
178 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
180 resultObject->setObject("inlineStyle", styleSheet->buildObjectForStyle(element->style()));
182 RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(element, true); // Support the viewing of :visited information in computed style.
183 RefPtr<InspectorStyle> computedInspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
184 resultObject->setObject("computedStyle", computedInspectorStyle->buildObjectForStyle());
186 CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
187 RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, false, true);
188 resultObject->setArray("matchedCSSRules", buildArrayForRuleList(matchedRules.get()));
190 resultObject->setObject("styleAttributes", buildObjectForAttributeStyles(element));
192 RefPtr<InspectorArray> pseudoElements = InspectorArray::create();
193 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
194 RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, false, true);
195 if (matchedRules && matchedRules->length()) {
196 RefPtr<InspectorObject> pseudoStyles = InspectorObject::create();
197 pseudoStyles->setNumber("pseudoId", static_cast<int>(pseudoId));
198 pseudoStyles->setArray("rules", buildArrayForRuleList(matchedRules.get()));
199 pseudoElements->pushObject(pseudoStyles.release());
202 resultObject->setArray("pseudoElements", pseudoElements.release());
204 RefPtr<InspectorArray> inheritedStyles = InspectorArray::create();
205 Element* parentElement = element->parentElement();
206 while (parentElement) {
207 RefPtr<InspectorObject> parentStyle = InspectorObject::create();
208 if (parentElement->style() && parentElement->style()->length()) {
209 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
211 parentStyle->setObject("inlineStyle", styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
214 CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector();
215 RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, false, true);
216 parentStyle->setArray("matchedCSSRules", buildArrayForRuleList(parentMatchedRules.get()));
217 inheritedStyles->pushObject(parentStyle.release());
218 parentElement = parentElement->parentElement();
220 resultObject->setArray("inherited", inheritedStyles.release());
222 *result = resultObject.release();
225 void InspectorCSSAgent::getInlineStyleForNode(ErrorString*, long nodeId, RefPtr<InspectorValue>* style)
227 Element* element = elementForId(nodeId);
231 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
235 *style = styleSheet->buildObjectForStyle(element->style());
238 void InspectorCSSAgent::getComputedStyleForNode(ErrorString*, long nodeId, RefPtr<InspectorValue>* style)
240 Element* element = elementForId(nodeId);
244 RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(element, true);
245 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
246 *style = inspectorStyle->buildObjectForStyle();
249 void InspectorCSSAgent::getAllStyles(ErrorString*, RefPtr<InspectorArray>* styles)
251 Vector<Document*> documents = m_domAgent->documents();
252 for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it) {
253 StyleSheetList* list = (*it)->styleSheets();
254 for (unsigned i = 0; i < list->length(); ++i) {
255 StyleSheet* styleSheet = list->item(i);
256 if (styleSheet->isCSSStyleSheet()) {
257 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(static_cast<CSSStyleSheet*>(styleSheet));
258 (*styles)->pushString(inspectorStyleSheet->id());
264 void InspectorCSSAgent::getStyleSheet(ErrorString*, const String& styleSheetId, RefPtr<InspectorValue>* styleSheetObject)
266 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(styleSheetId);
267 if (!inspectorStyleSheet)
270 *styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet();
273 void InspectorCSSAgent::getStyleSheetText(ErrorString*, const String& styleSheetId, String* url, String* result)
275 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(styleSheetId);
276 if (!inspectorStyleSheet)
278 *url = inspectorStyleSheet->finalURL();
279 inspectorStyleSheet->text(result);
282 void InspectorCSSAgent::setStyleSheetText(ErrorString*, const String& styleSheetId, const String& text, bool* success)
284 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(styleSheetId);
285 if (!inspectorStyleSheet) {
290 *success = inspectorStyleSheet->setText(text);
292 inspectorStyleSheet->reparseStyleSheet(text);
295 void InspectorCSSAgent::setPropertyText(ErrorString*, const RefPtr<InspectorObject>& fullStyleId, long propertyIndex, const String& text, bool overwrite, RefPtr<InspectorValue>* result)
297 InspectorCSSId compoundId(fullStyleId);
298 ASSERT(!compoundId.isEmpty());
300 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(compoundId.styleSheetId());
301 if (!inspectorStyleSheet)
304 bool success = inspectorStyleSheet->setPropertyText(compoundId, propertyIndex, text, overwrite);
306 *result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
309 void InspectorCSSAgent::toggleProperty(ErrorString*, const RefPtr<InspectorObject>& fullStyleId, long propertyIndex, bool disable, RefPtr<InspectorValue>* result)
311 InspectorCSSId compoundId(fullStyleId);
312 ASSERT(!compoundId.isEmpty());
314 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(compoundId.styleSheetId());
315 if (!inspectorStyleSheet)
318 bool success = inspectorStyleSheet->toggleProperty(compoundId, propertyIndex, disable);
320 *result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
323 void InspectorCSSAgent::setRuleSelector(ErrorString*, const RefPtr<InspectorObject>& fullRuleId, const String& selector, RefPtr<InspectorValue>* result)
325 InspectorCSSId compoundId(fullRuleId);
326 ASSERT(!compoundId.isEmpty());
328 InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(compoundId.styleSheetId());
329 if (!inspectorStyleSheet)
332 bool success = inspectorStyleSheet->setRuleSelector(compoundId, selector);
336 *result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(compoundId));
339 void InspectorCSSAgent::addRule(ErrorString*, const long contextNodeId, const String& selector, RefPtr<InspectorValue>* result)
341 Node* node = m_domAgent->nodeForId(contextNodeId);
345 InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(node->document(), true);
346 if (!inspectorStyleSheet)
348 CSSStyleRule* newRule = inspectorStyleSheet->addRule(selector);
352 *result = inspectorStyleSheet->buildObjectForRule(newRule);
355 void InspectorCSSAgent::getSupportedCSSProperties(ErrorString*, RefPtr<InspectorArray>* cssProperties)
357 RefPtr<InspectorArray> properties = InspectorArray::create();
358 for (int i = 0; i < numCSSProperties; ++i)
359 properties->pushString(propertyNameStrings[i]);
361 *cssProperties = properties.release();
364 void InspectorCSSAgent::querySelectorAll(ErrorString*, const long nodeId, const String& selector, RefPtr<InspectorArray>* result)
366 Node* node = m_domAgent->nodeForId(nodeId);
369 if (!node->isDocumentNode())
370 node = node->ownerDocument();
373 ExceptionCode ec = 0;
374 RefPtr<NodeList> nodes = static_cast<Document*>(node)->querySelectorAll(selector, ec);
377 for (unsigned i = 0; i < nodes->length(); ++i) {
378 Node* affectedNode = nodes->item(i);
379 long id = m_domAgent->pushNodePathToFrontend(affectedNode);
380 (*result)->pushNumber(id);
385 Element* InspectorCSSAgent::inlineStyleElement(CSSStyleDeclaration* style)
387 if (!style || !style->isMutableStyleDeclaration())
389 Node* node = static_cast<CSSMutableStyleDeclaration*>(style)->node();
390 if (!node || !node->isStyledElement() || static_cast<StyledElement*>(node)->getInlineStyleDecl() != style)
392 return static_cast<Element*>(node);
395 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
397 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
398 if (it == m_nodeToInspectorStyleSheet.end()) {
399 CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0;
403 String newStyleSheetId = String::number(m_lastStyleSheetId++);
404 RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, "");
405 m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet);
406 m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
407 return inspectorStyleSheet.get();
410 return it->second.get();
413 Element* InspectorCSSAgent::elementForId(long nodeId)
415 Node* node = m_domAgent->nodeForId(nodeId);
416 return (!node || node->nodeType() != Node::ELEMENT_NODE) ? 0 : static_cast<Element*>(node);
419 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
421 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
422 if (!inspectorStyleSheet) {
423 String id = String::number(m_lastStyleSheetId++);
424 inspectorStyleSheet = InspectorStyleSheet::create(id, styleSheet, detectOrigin(styleSheet, styleSheet->document()), m_domAgent->documentURLString(styleSheet->document()));
425 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
426 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
428 return inspectorStyleSheet.get();
431 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
434 ASSERT(!createIfAbsent);
438 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document);
439 if (inspectorStyleSheet || !createIfAbsent)
440 return inspectorStyleSheet.get();
442 ExceptionCode ec = 0;
443 RefPtr<Element> styleElement = document->createElement("style", ec);
445 styleElement->setAttribute("type", "text/css", ec);
447 ContainerNode* targetNode;
448 // HEAD is absent in ImageDocuments, for example.
449 if (document->head())
450 targetNode = document->head();
451 else if (document->body())
452 targetNode = document->body();
455 targetNode->appendChild(styleElement, ec);
459 StyleSheetList* styleSheets = document->styleSheets();
460 StyleSheet* styleSheet = styleSheets->item(styleSheets->length() - 1);
461 if (!styleSheet->isCSSStyleSheet())
463 CSSStyleSheet* cssStyleSheet = static_cast<CSSStyleSheet*>(styleSheet);
464 String id = String::number(m_lastStyleSheetId++);
465 inspectorStyleSheet = InspectorStyleSheet::create(id, cssStyleSheet, "inspector", m_domAgent->documentURLString(document));
466 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
467 m_cssStyleSheetToInspectorStyleSheet.set(cssStyleSheet, inspectorStyleSheet);
468 m_documentToInspectorStyleSheet.set(document, inspectorStyleSheet);
469 return inspectorStyleSheet.get();
472 InspectorStyleSheet* InspectorCSSAgent::styleSheetForId(const String& styleSheetId)
474 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
475 return it == m_idToInspectorStyleSheet.end() ? 0 : it->second.get();
478 String InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
480 DEFINE_STATIC_LOCAL(String, userAgent, ("user-agent"));
481 DEFINE_STATIC_LOCAL(String, user, ("user"));
482 DEFINE_STATIC_LOCAL(String, inspector, ("inspector"));
485 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
487 else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
490 InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
491 if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
497 PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList)
499 RefPtr<InspectorArray> result = InspectorArray::create();
501 return result.release();
503 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
504 CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
508 InspectorStyleSheet* styleSheet = bindStyleSheet(parentStyleSheet(rule));
510 result->pushObject(styleSheet->buildObjectForRule(rule));
512 return result.release();
515 PassRefPtr<InspectorObject> InspectorCSSAgent::buildObjectForAttributeStyles(Element* element)
517 RefPtr<InspectorObject> styleAttributes = InspectorObject::create();
518 NamedNodeMap* attributes = element->attributes();
519 for (unsigned i = 0; attributes && i < attributes->length(); ++i) {
520 Attribute* attribute = attributes->attributeItem(i);
521 if (attribute->style()) {
522 String attributeName = attribute->localName();
523 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), attribute->style(), 0);
524 styleAttributes->setObject(attributeName.utf8().data(), inspectorStyle->buildObjectForStyle());
528 return styleAttributes;
531 void InspectorCSSAgent::didRemoveDocument(Document* document)
534 m_documentToInspectorStyleSheet.remove(document);
537 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
542 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
543 if (it == m_nodeToInspectorStyleSheet.end())
546 m_idToInspectorStyleSheet.remove(it->second->id());
547 m_nodeToInspectorStyleSheet.remove(node);
550 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
555 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
556 if (it == m_nodeToInspectorStyleSheet.end())
559 it->second->didModifyElementAttribute();
562 } // namespace WebCore
564 #endif // ENABLE(INSPECTOR)