1 /* 2 * Copyright (C) 2010, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 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. 12 * 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. 23 */ 24 25 #include "config.h" 26 #include "core/inspector/InspectorCSSAgent.h" 27 28 #include "bindings/v8/ExceptionState.h" 29 #include "bindings/v8/ExceptionStatePlaceholder.h" 30 #include "core/CSSPropertyNames.h" 31 #include "core/InspectorTypeBuilder.h" 32 #include "core/StylePropertyShorthand.h" 33 #include "core/css/CSSComputedStyleDeclaration.h" 34 #include "core/css/CSSDefaultStyleSheets.h" 35 #include "core/css/CSSImportRule.h" 36 #include "core/css/CSSMediaRule.h" 37 #include "core/css/CSSRule.h" 38 #include "core/css/CSSRuleList.h" 39 #include "core/css/CSSStyleRule.h" 40 #include "core/css/CSSStyleSheet.h" 41 #include "core/css/MediaList.h" 42 #include "core/css/MediaQuery.h" 43 #include "core/css/MediaValues.h" 44 #include "core/css/StylePropertySet.h" 45 #include "core/css/StyleRule.h" 46 #include "core/css/StyleSheet.h" 47 #include "core/css/StyleSheetContents.h" 48 #include "core/css/StyleSheetList.h" 49 #include "core/css/resolver/StyleResolver.h" 50 #include "core/dom/Node.h" 51 #include "core/frame/LocalFrame.h" 52 #include "core/html/HTMLHeadElement.h" 53 #include "core/html/VoidCallback.h" 54 #include "core/inspector/InspectorHistory.h" 55 #include "core/inspector/InspectorPageAgent.h" 56 #include "core/inspector/InspectorResourceAgent.h" 57 #include "core/inspector/InspectorResourceContentLoader.h" 58 #include "core/inspector/InspectorState.h" 59 #include "core/inspector/InstrumentingAgents.h" 60 #include "core/loader/DocumentLoader.h" 61 #include "core/page/Page.h" 62 #include "core/rendering/InlineTextBox.h" 63 #include "core/rendering/RenderObject.h" 64 #include "core/rendering/RenderText.h" 65 #include "core/rendering/RenderTextFragment.h" 66 #include "platform/fonts/Font.h" 67 #include "platform/fonts/GlyphBuffer.h" 68 #include "platform/fonts/WidthIterator.h" 69 #include "platform/text/TextRun.h" 70 #include "wtf/CurrentTime.h" 71 #include "wtf/text/CString.h" 72 #include "wtf/text/StringConcatenate.h" 73 74 namespace CSSAgentState { 75 static const char cssAgentEnabled[] = "cssAgentEnabled"; 76 } 77 78 typedef WebCore::InspectorBackendDispatcher::CSSCommandHandler::EnableCallback EnableCallback; 79 80 namespace WebCore { 81 82 enum ForcePseudoClassFlags { 83 PseudoNone = 0, 84 PseudoHover = 1 << 0, 85 PseudoFocus = 1 << 1, 86 PseudoActive = 1 << 2, 87 PseudoVisited = 1 << 3 88 }; 89 90 static unsigned computePseudoClassMask(JSONArray* pseudoClassArray) 91 { 92 DEFINE_STATIC_LOCAL(String, active, ("active")); 93 DEFINE_STATIC_LOCAL(String, hover, ("hover")); 94 DEFINE_STATIC_LOCAL(String, focus, ("focus")); 95 DEFINE_STATIC_LOCAL(String, visited, ("visited")); 96 if (!pseudoClassArray || !pseudoClassArray->length()) 97 return PseudoNone; 98 99 unsigned result = PseudoNone; 100 for (size_t i = 0; i < pseudoClassArray->length(); ++i) { 101 RefPtr<JSONValue> pseudoClassValue = pseudoClassArray->get(i); 102 String pseudoClass; 103 bool success = pseudoClassValue->asString(&pseudoClass); 104 if (!success) 105 continue; 106 if (pseudoClass == active) 107 result |= PseudoActive; 108 else if (pseudoClass == hover) 109 result |= PseudoHover; 110 else if (pseudoClass == focus) 111 result |= PseudoFocus; 112 else if (pseudoClass == visited) 113 result |= PseudoVisited; 114 } 115 116 return result; 117 } 118 119 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action { 120 WTF_MAKE_NONCOPYABLE(StyleSheetAction); 121 public: 122 StyleSheetAction(const String& name) 123 : InspectorHistory::Action(name) 124 { 125 } 126 }; 127 128 class InspectorCSSAgent::InspectorResourceContentLoaderCallback FINAL : public VoidCallback { 129 public: 130 InspectorResourceContentLoaderCallback(InspectorCSSAgent*, PassRefPtr<EnableCallback>); 131 virtual void handleEvent() OVERRIDE; 132 133 private: 134 InspectorCSSAgent* m_cssAgent; 135 RefPtr<EnableCallback> m_callback; 136 }; 137 138 InspectorCSSAgent::InspectorResourceContentLoaderCallback::InspectorResourceContentLoaderCallback(InspectorCSSAgent* cssAgent, PassRefPtr<EnableCallback> callback) 139 : m_cssAgent(cssAgent) 140 , m_callback(callback) 141 { 142 } 143 144 void InspectorCSSAgent::InspectorResourceContentLoaderCallback::handleEvent() 145 { 146 // enable always succeeds. 147 if (!m_callback->isActive()) 148 return; 149 150 m_cssAgent->wasEnabled(); 151 m_callback->sendSuccess(); 152 } 153 154 class InspectorCSSAgent::SetStyleSheetTextAction FINAL : public InspectorCSSAgent::StyleSheetAction { 155 WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction); 156 public: 157 SetStyleSheetTextAction(InspectorStyleSheetBase* styleSheet, const String& text) 158 : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText") 159 , m_styleSheet(styleSheet) 160 , m_text(text) 161 { 162 } 163 164 virtual bool perform(ExceptionState& exceptionState) OVERRIDE 165 { 166 if (!m_styleSheet->getText(&m_oldText)) 167 return false; 168 return redo(exceptionState); 169 } 170 171 virtual bool undo(ExceptionState& exceptionState) OVERRIDE 172 { 173 return m_styleSheet->setText(m_oldText, exceptionState); 174 } 175 176 virtual bool redo(ExceptionState& exceptionState) OVERRIDE 177 { 178 return m_styleSheet->setText(m_text, exceptionState); 179 } 180 181 virtual String mergeId() OVERRIDE 182 { 183 return String::format("SetStyleSheetText %s", m_styleSheet->id().utf8().data()); 184 } 185 186 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) OVERRIDE 187 { 188 ASSERT(action->mergeId() == mergeId()); 189 190 SetStyleSheetTextAction* other = static_cast<SetStyleSheetTextAction*>(action.get()); 191 m_text = other->m_text; 192 } 193 194 private: 195 RefPtr<InspectorStyleSheetBase> m_styleSheet; 196 String m_text; 197 String m_oldText; 198 }; 199 200 class InspectorCSSAgent::SetPropertyTextAction FINAL : public InspectorCSSAgent::StyleSheetAction { 201 WTF_MAKE_NONCOPYABLE(SetPropertyTextAction); 202 public: 203 SetPropertyTextAction(InspectorStyleSheetBase* styleSheet, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite) 204 : InspectorCSSAgent::StyleSheetAction("SetPropertyText") 205 , m_styleSheet(styleSheet) 206 , m_cssId(cssId) 207 , m_propertyIndex(propertyIndex) 208 , m_text(text) 209 , m_overwrite(overwrite) 210 { 211 } 212 213 virtual String toString() OVERRIDE 214 { 215 return mergeId() + ": " + m_oldStyleText + " -> " + m_text; 216 } 217 218 virtual bool perform(ExceptionState& exceptionState) OVERRIDE 219 { 220 return redo(exceptionState); 221 } 222 223 virtual bool undo(ExceptionState& exceptionState) OVERRIDE 224 { 225 String placeholder; 226 return m_styleSheet->setStyleText(m_cssId, m_oldStyleText); 227 } 228 229 virtual bool redo(ExceptionState& exceptionState) OVERRIDE 230 { 231 if (!m_styleSheet->getStyleText(m_cssId, &m_oldStyleText)) 232 return false; 233 bool result = m_styleSheet->setPropertyText(m_cssId, m_propertyIndex, m_text, m_overwrite, exceptionState); 234 return result; 235 } 236 237 virtual String mergeId() OVERRIDE 238 { 239 return String::format("SetPropertyText %s:%u:%s", m_styleSheet->id().utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false"); 240 } 241 242 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) OVERRIDE 243 { 244 ASSERT(action->mergeId() == mergeId()); 245 246 SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get()); 247 m_text = other->m_text; 248 } 249 250 private: 251 RefPtr<InspectorStyleSheetBase> m_styleSheet; 252 InspectorCSSId m_cssId; 253 unsigned m_propertyIndex; 254 String m_text; 255 String m_oldStyleText; 256 bool m_overwrite; 257 }; 258 259 class InspectorCSSAgent::SetRuleSelectorAction FINAL : public InspectorCSSAgent::StyleSheetAction { 260 WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction); 261 public: 262 SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector) 263 : InspectorCSSAgent::StyleSheetAction("SetRuleSelector") 264 , m_styleSheet(styleSheet) 265 , m_cssId(cssId) 266 , m_selector(selector) 267 { 268 } 269 270 virtual bool perform(ExceptionState& exceptionState) OVERRIDE 271 { 272 m_oldSelector = m_styleSheet->ruleSelector(m_cssId, exceptionState); 273 if (exceptionState.hadException()) 274 return false; 275 return redo(exceptionState); 276 } 277 278 virtual bool undo(ExceptionState& exceptionState) OVERRIDE 279 { 280 return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector, exceptionState); 281 } 282 283 virtual bool redo(ExceptionState& exceptionState) OVERRIDE 284 { 285 return m_styleSheet->setRuleSelector(m_cssId, m_selector, exceptionState); 286 } 287 288 private: 289 RefPtr<InspectorStyleSheet> m_styleSheet; 290 InspectorCSSId m_cssId; 291 String m_selector; 292 String m_oldSelector; 293 }; 294 295 class InspectorCSSAgent::AddRuleAction FINAL : public InspectorCSSAgent::StyleSheetAction { 296 WTF_MAKE_NONCOPYABLE(AddRuleAction); 297 public: 298 AddRuleAction(InspectorStyleSheet* styleSheet, const String& selector) 299 : InspectorCSSAgent::StyleSheetAction("AddRule") 300 , m_styleSheet(styleSheet) 301 , m_selector(selector) 302 { 303 } 304 305 virtual bool perform(ExceptionState& exceptionState) OVERRIDE 306 { 307 return redo(exceptionState); 308 } 309 310 virtual bool undo(ExceptionState& exceptionState) OVERRIDE 311 { 312 return m_styleSheet->deleteRule(m_newId, exceptionState); 313 } 314 315 virtual bool redo(ExceptionState& exceptionState) OVERRIDE 316 { 317 CSSStyleRule* cssStyleRule = m_styleSheet->addRule(m_selector, exceptionState); 318 if (exceptionState.hadException()) 319 return false; 320 m_newId = m_styleSheet->ruleId(cssStyleRule); 321 return true; 322 } 323 324 InspectorCSSId newRuleId() { return m_newId; } 325 326 private: 327 RefPtr<InspectorStyleSheet> m_styleSheet; 328 InspectorCSSId m_newId; 329 String m_selector; 330 String m_oldSelector; 331 }; 332 333 // static 334 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule) 335 { 336 if (!rule || rule->type() != CSSRule::STYLE_RULE) 337 return 0; 338 return toCSSStyleRule(rule); 339 } 340 341 InspectorCSSAgent::InspectorCSSAgent(InspectorDOMAgent* domAgent, InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent) 342 : InspectorBaseAgent<InspectorCSSAgent>("CSS") 343 , m_frontend(0) 344 , m_domAgent(domAgent) 345 , m_pageAgent(pageAgent) 346 , m_resourceAgent(resourceAgent) 347 , m_lastStyleSheetId(1) 348 , m_styleSheetsPendingMutation(0) 349 , m_styleDeclarationPendingMutation(false) 350 , m_creatingViaInspectorStyleSheet(false) 351 , m_isSettingStyleSheetText(false) 352 { 353 m_domAgent->setDOMListener(this); 354 } 355 356 InspectorCSSAgent::~InspectorCSSAgent() 357 { 358 ASSERT(!m_domAgent); 359 reset(); 360 } 361 362 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend) 363 { 364 ASSERT(!m_frontend); 365 m_frontend = frontend->css(); 366 } 367 368 void InspectorCSSAgent::clearFrontend() 369 { 370 ASSERT(m_frontend); 371 ErrorString error; 372 disable(&error); 373 m_frontend = 0; 374 resetNonPersistentData(); 375 } 376 377 void InspectorCSSAgent::discardAgent() 378 { 379 m_domAgent->setDOMListener(0); 380 m_domAgent = 0; 381 } 382 383 void InspectorCSSAgent::restore() 384 { 385 if (m_state->getBoolean(CSSAgentState::cssAgentEnabled)) 386 wasEnabled(); 387 } 388 389 void InspectorCSSAgent::flushPendingFrontendMessages() 390 { 391 if (!m_invalidatedDocuments.size()) 392 return; 393 HashSet<Document*> invalidatedDocuments; 394 m_invalidatedDocuments.swap(&invalidatedDocuments); 395 for (HashSet<Document*>::iterator it = invalidatedDocuments.begin(); it != invalidatedDocuments.end(); ++it) 396 updateActiveStyleSheets(*it, ExistingFrontendRefresh); 397 } 398 399 void InspectorCSSAgent::reset() 400 { 401 m_idToInspectorStyleSheet.clear(); 402 m_idToInspectorStyleSheetForInlineStyle.clear(); 403 m_cssStyleSheetToInspectorStyleSheet.clear(); 404 m_documentToCSSStyleSheets.clear(); 405 m_invalidatedDocuments.clear(); 406 m_nodeToInspectorStyleSheet.clear(); 407 m_documentToViaInspectorStyleSheet.clear(); 408 resetNonPersistentData(); 409 } 410 411 void InspectorCSSAgent::resetNonPersistentData() 412 { 413 resetPseudoStates(); 414 } 415 416 void InspectorCSSAgent::enable(ErrorString*, PassRefPtr<EnableCallback> prpCallback) 417 { 418 m_state->setBoolean(CSSAgentState::cssAgentEnabled, true); 419 if (!m_pageAgent->resourceContentLoader()) { 420 wasEnabled(); 421 prpCallback->sendSuccess(); 422 return; 423 } 424 m_pageAgent->resourceContentLoader()->addListener(adoptPtr(new InspectorCSSAgent::InspectorResourceContentLoaderCallback(this, prpCallback))); 425 } 426 427 void InspectorCSSAgent::wasEnabled() 428 { 429 if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) { 430 // We were disabled while fetching resources. 431 return; 432 } 433 434 m_instrumentingAgents->setInspectorCSSAgent(this); 435 Vector<Document*> documents = m_domAgent->documents(); 436 for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it) 437 updateActiveStyleSheets(*it, InitialFrontendLoad); 438 } 439 440 void InspectorCSSAgent::disable(ErrorString*) 441 { 442 reset(); 443 m_instrumentingAgents->setInspectorCSSAgent(0); 444 m_state->setBoolean(CSSAgentState::cssAgentEnabled, false); 445 } 446 447 void InspectorCSSAgent::didCommitLoadForMainFrame() 448 { 449 reset(); 450 } 451 452 void InspectorCSSAgent::mediaQueryResultChanged() 453 { 454 flushPendingFrontendMessages(); 455 m_frontend->mediaQueryResultChanged(); 456 } 457 458 void InspectorCSSAgent::willMutateRules() 459 { 460 ++m_styleSheetsPendingMutation; 461 } 462 463 void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet) 464 { 465 --m_styleSheetsPendingMutation; 466 ASSERT(m_styleSheetsPendingMutation >= 0); 467 468 if (!styleSheetEditInProgress()) { 469 Document* owner = styleSheet->ownerDocument(); 470 if (owner) 471 owner->modifiedStyleSheet(styleSheet, FullStyleUpdate); 472 } 473 } 474 475 void InspectorCSSAgent::willMutateStyle() 476 { 477 m_styleDeclarationPendingMutation = true; 478 } 479 480 void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle) 481 { 482 ASSERT(m_styleDeclarationPendingMutation); 483 m_styleDeclarationPendingMutation = false; 484 if (!styleSheetEditInProgress() && !isInlineStyle) { 485 CSSStyleSheet* parentSheet = style->parentStyleSheet(); 486 Document* owner = parentSheet ? parentSheet->ownerDocument() : 0; 487 if (owner) 488 owner->modifiedStyleSheet(parentSheet, FullStyleUpdate); 489 } 490 } 491 492 void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document) 493 { 494 if (styleSheetEditInProgress()) 495 return; 496 m_invalidatedDocuments.add(document); 497 if (m_creatingViaInspectorStyleSheet) 498 flushPendingFrontendMessages(); 499 } 500 501 void InspectorCSSAgent::updateActiveStyleSheets(Document* document, StyleSheetsUpdateType styleSheetsUpdateType) 502 { 503 Vector<CSSStyleSheet*> newSheetsVector; 504 InspectorCSSAgent::collectAllDocumentStyleSheets(document, newSheetsVector); 505 setActiveStyleSheets(document, newSheetsVector, styleSheetsUpdateType); 506 } 507 508 void InspectorCSSAgent::setActiveStyleSheets(Document* document, const Vector<CSSStyleSheet*>& allSheetsVector, StyleSheetsUpdateType styleSheetsUpdateType) 509 { 510 bool isInitialFrontendLoad = styleSheetsUpdateType == InitialFrontendLoad; 511 512 HashSet<CSSStyleSheet*>* documentCSSStyleSheets = m_documentToCSSStyleSheets.get(document); 513 if (!documentCSSStyleSheets) { 514 documentCSSStyleSheets = new HashSet<CSSStyleSheet*>(); 515 OwnPtr<HashSet<CSSStyleSheet*> > documentCSSStyleSheetsPtr = adoptPtr(documentCSSStyleSheets); 516 m_documentToCSSStyleSheets.set(document, documentCSSStyleSheetsPtr.release()); 517 } 518 519 HashSet<CSSStyleSheet*> removedSheets(*documentCSSStyleSheets); 520 Vector<CSSStyleSheet*> addedSheets; 521 for (Vector<CSSStyleSheet*>::const_iterator it = allSheetsVector.begin(); it != allSheetsVector.end(); ++it) { 522 CSSStyleSheet* cssStyleSheet = *it; 523 if (removedSheets.contains(cssStyleSheet)) { 524 removedSheets.remove(cssStyleSheet); 525 if (isInitialFrontendLoad) 526 addedSheets.append(cssStyleSheet); 527 } else { 528 addedSheets.append(cssStyleSheet); 529 } 530 } 531 532 for (HashSet<CSSStyleSheet*>::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) { 533 CSSStyleSheet* cssStyleSheet = *it; 534 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet); 535 ASSERT(inspectorStyleSheet); 536 537 documentCSSStyleSheets->remove(cssStyleSheet); 538 if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) { 539 String id = unbindStyleSheet(inspectorStyleSheet.get()); 540 if (m_frontend && !isInitialFrontendLoad) 541 m_frontend->styleSheetRemoved(id); 542 } 543 } 544 545 for (Vector<CSSStyleSheet*>::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) { 546 CSSStyleSheet* cssStyleSheet = *it; 547 bool isNew = isInitialFrontendLoad || !m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet); 548 if (isNew) { 549 InspectorStyleSheet* newStyleSheet = bindStyleSheet(cssStyleSheet); 550 documentCSSStyleSheets->add(cssStyleSheet); 551 if (m_frontend) 552 m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo()); 553 } 554 } 555 556 if (documentCSSStyleSheets->isEmpty()) 557 m_documentToCSSStyleSheets.remove(document); 558 } 559 560 void InspectorCSSAgent::documentDetached(Document* document) 561 { 562 m_invalidatedDocuments.remove(document); 563 setActiveStyleSheets(document, Vector<CSSStyleSheet*>(), ExistingFrontendRefresh); 564 } 565 566 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType) 567 { 568 if (m_nodeIdToForcedPseudoState.isEmpty()) 569 return false; 570 571 int nodeId = m_domAgent->boundNodeId(element); 572 if (!nodeId) 573 return false; 574 575 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId); 576 if (it == m_nodeIdToForcedPseudoState.end()) 577 return false; 578 579 unsigned forcedPseudoState = it->value; 580 switch (pseudoType) { 581 case CSSSelector::PseudoActive: 582 return forcedPseudoState & PseudoActive; 583 case CSSSelector::PseudoFocus: 584 return forcedPseudoState & PseudoFocus; 585 case CSSSelector::PseudoHover: 586 return forcedPseudoState & PseudoHover; 587 case CSSSelector::PseudoVisited: 588 return forcedPseudoState & PseudoVisited; 589 default: 590 return false; 591 } 592 } 593 594 void InspectorCSSAgent::getMediaQueries(ErrorString* errorString, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> >& medias) 595 { 596 medias = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create(); 597 for (IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.begin(); it != m_idToInspectorStyleSheet.end(); ++it) { 598 RefPtr<InspectorStyleSheet> styleSheet = it->value; 599 collectMediaQueriesFromStyleSheet(styleSheet->pageStyleSheet(), medias.get()); 600 const CSSRuleVector& flatRules = styleSheet->flatRules(); 601 for (unsigned i = 0; i < flatRules.size(); ++i) { 602 CSSRule* rule = flatRules.at(i).get(); 603 if (rule->type() == CSSRule::MEDIA_RULE) 604 collectMediaQueriesFromRule(rule, medias.get()); 605 } 606 } 607 } 608 609 void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int nodeId, const bool* includePseudo, const bool* includeInherited, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> >& matchedCSSRules, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> >& pseudoIdMatches, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> >& inheritedEntries) 610 { 611 Element* element = elementForId(errorString, nodeId); 612 if (!element) { 613 *errorString = "Node not found"; 614 return; 615 } 616 617 Element* originalElement = element; 618 PseudoId elementPseudoId = element->pseudoId(); 619 if (elementPseudoId) { 620 element = element->parentOrShadowHostElement(); 621 if (!element) { 622 *errorString = "Pseudo element has no parent"; 623 return; 624 } 625 } 626 627 Document* ownerDocument = element->ownerDocument(); 628 // A non-active document has no styles. 629 if (!ownerDocument->isActive()) 630 return; 631 632 // FIXME: It's really gross for the inspector to reach in and access StyleResolver 633 // directly here. We need to provide the Inspector better APIs to get this information 634 // without grabbing at internal style classes! 635 636 // Matched rules. 637 StyleResolver& styleResolver = ownerDocument->ensureStyleResolver(); 638 639 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules); 640 matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), originalElement); 641 642 // Pseudo elements. 643 if (!elementPseudoId && (!includePseudo || *includePseudo)) { 644 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create(); 645 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { 646 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules); 647 if (matchedRules && matchedRules->length()) { 648 RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create() 649 .setPseudoId(static_cast<int>(pseudoId)) 650 .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), element)); 651 pseudoElements->addItem(matches.release()); 652 } 653 } 654 655 pseudoIdMatches = pseudoElements.release(); 656 } 657 658 // Inherited styles. 659 if (!elementPseudoId && (!includeInherited || *includeInherited)) { 660 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create(); 661 Element* parentElement = element->parentElement(); 662 while (parentElement) { 663 StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver(); 664 RefPtrWillBeRawPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules); 665 RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create() 666 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), parentElement)); 667 if (parentElement->style() && parentElement->style()->length()) { 668 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement); 669 if (styleSheet) 670 entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0)))); 671 } 672 673 entries->addItem(entry.release()); 674 parentElement = parentElement->parentElement(); 675 } 676 677 inheritedEntries = entries.release(); 678 } 679 } 680 681 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle) 682 { 683 Element* element = elementForId(errorString, nodeId); 684 if (!element) 685 return; 686 687 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element); 688 if (!styleSheet) 689 return; 690 691 inlineStyle = styleSheet->buildObjectForStyle(element->style()); 692 RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element); 693 attributesStyle = attributes ? attributes.release() : nullptr; 694 } 695 696 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style) 697 { 698 Node* node = m_domAgent->assertNode(errorString, nodeId); 699 if (!node) 700 return; 701 702 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true); 703 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0); 704 style = inspectorStyle->buildArrayForComputedStyle(); 705 } 706 707 void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats) 708 { 709 for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) { 710 RenderStyle* style = renderer->style(box->isFirstLineStyle()); 711 const Font& font = style->font(); 712 TextRun run = box->constructTextRunForInspector(style, font); 713 WidthIterator it(&font, run, 0, false); 714 GlyphBuffer glyphBuffer; 715 it.advance(run.length(), &glyphBuffer); 716 for (unsigned i = 0; i < glyphBuffer.size(); ++i) { 717 String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName(); 718 if (familyName.isNull()) 719 familyName = ""; 720 fontStats->add(familyName); 721 } 722 } 723 } 724 725 void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId, 726 String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts) 727 { 728 Node* node = m_domAgent->assertNode(errorString, nodeId); 729 if (!node) 730 return; 731 732 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true); 733 *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily); 734 735 Vector<Node*> textNodes; 736 if (node->nodeType() == Node::TEXT_NODE) { 737 if (node->renderer()) 738 textNodes.append(node); 739 } else { 740 for (Node* child = node->firstChild(); child; child = child->nextSibling()) { 741 if (child->nodeType() == Node::TEXT_NODE && child->renderer()) 742 textNodes.append(child); 743 } 744 } 745 746 HashCountedSet<String> fontStats; 747 for (size_t i = 0; i < textNodes.size(); ++i) { 748 RenderText* renderer = toRenderText(textNodes[i]->renderer()); 749 collectPlatformFontsForRenderer(renderer, &fontStats); 750 if (renderer->isTextFragment()) { 751 RenderTextFragment* textFragment = toRenderTextFragment(renderer); 752 if (textFragment->firstLetter()) { 753 RenderBoxModelObject* firstLetter = textFragment->firstLetter(); 754 for (RenderObject* current = firstLetter->slowFirstChild(); current; current = current->nextSibling()) { 755 if (current->isText()) 756 collectPlatformFontsForRenderer(toRenderText(current), &fontStats); 757 } 758 } 759 } 760 } 761 762 platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create(); 763 for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) { 764 RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create() 765 .setFamilyName(it->key) 766 .setGlyphCount(it->value); 767 platformFonts->addItem(platformFont); 768 } 769 } 770 771 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result) 772 { 773 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); 774 if (!inspectorStyleSheet) 775 return; 776 777 inspectorStyleSheet->getText(result); 778 } 779 780 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text) 781 { 782 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); 783 if (!inspectorStyleSheet) { 784 *errorString = "Style sheet with id " + styleSheetId + " not found"; 785 return; 786 } 787 788 TrackExceptionState exceptionState; 789 m_domAgent->history()->perform(adoptRefWillBeNoop(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState); 790 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 791 } 792 793 static bool extractRangeComponent(ErrorString* errorString, const RefPtr<JSONObject>& range, const String& component, unsigned& result) 794 { 795 int parsedValue; 796 if (!range->getNumber(component, &parsedValue) || parsedValue < 0) { 797 *errorString = "range." + component + " must be a non-negative integer"; 798 return false; 799 } 800 result = parsedValue; 801 return true; 802 } 803 804 static bool jsonRangeToSourceRange(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const RefPtr<JSONObject>& range, SourceRange* sourceRange) 805 { 806 unsigned startLineNumber; 807 unsigned startColumn; 808 unsigned endLineNumber; 809 unsigned endColumn; 810 if (!extractRangeComponent(errorString, range, "startLine", startLineNumber) 811 || !extractRangeComponent(errorString, range, "startColumn", startColumn) 812 || !extractRangeComponent(errorString, range, "endLine", endLineNumber) 813 || !extractRangeComponent(errorString, range, "endColumn", endColumn)) 814 return false; 815 816 unsigned startOffset; 817 unsigned endOffset; 818 bool success = inspectorStyleSheet->lineNumberAndColumnToOffset(startLineNumber, startColumn, &startOffset) 819 && inspectorStyleSheet->lineNumberAndColumnToOffset(endLineNumber, endColumn, &endOffset); 820 if (!success) { 821 *errorString = "Specified range is out of bounds"; 822 return false; 823 } 824 825 if (startOffset > endOffset) { 826 *errorString = "Range start must not succeed its end"; 827 return false; 828 } 829 sourceRange->start = startOffset; 830 sourceRange->end = endOffset; 831 return true; 832 } 833 834 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result) 835 { 836 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); 837 if (!inspectorStyleSheet) 838 return; 839 SourceRange propertyRange; 840 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &propertyRange)) 841 return; 842 InspectorCSSId compoundId; 843 unsigned propertyIndex; 844 bool overwrite; 845 if (!inspectorStyleSheet->findPropertyByRange(propertyRange, &compoundId, &propertyIndex, &overwrite)) { 846 *errorString = "Source range didn't match any existing property source range nor any property insertion point"; 847 return; 848 } 849 850 TrackExceptionState exceptionState; 851 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState); 852 if (success) 853 result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId)); 854 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 855 } 856 857 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result) 858 { 859 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId); 860 if (!inspectorStyleSheet) 861 return; 862 SourceRange selectorRange; 863 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange)) 864 return; 865 InspectorCSSId compoundId; 866 if (!inspectorStyleSheet->findRuleBySelectorRange(selectorRange, &compoundId)) { 867 *errorString = "Source range didn't match any rule selector source range"; 868 return; 869 } 870 871 TrackExceptionState exceptionState; 872 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState); 873 if (success) { 874 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId); 875 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule)); 876 } 877 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 878 } 879 880 void InspectorCSSAgent::createStyleSheet(ErrorString* errorString, const String& frameId, TypeBuilder::CSS::StyleSheetId* outStyleSheetId) 881 { 882 LocalFrame* frame = m_pageAgent->frameForId(frameId); 883 if (!frame) { 884 *errorString = "Frame not found"; 885 return; 886 } 887 888 Document* document = frame->document(); 889 if (!document) { 890 *errorString = "Frame does not have a document"; 891 return; 892 } 893 894 InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(document, true); 895 if (!inspectorStyleSheet) { 896 *errorString = "No target stylesheet found"; 897 return; 898 } 899 900 updateActiveStyleSheets(document, ExistingFrontendRefresh); 901 902 *outStyleSheetId = inspectorStyleSheet->id(); 903 } 904 905 void InspectorCSSAgent::addRule(ErrorString* errorString, const String& styleSheetId, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result) 906 { 907 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId); 908 if (!inspectorStyleSheet) 909 return; 910 911 TrackExceptionState exceptionState; 912 RefPtrWillBeRawPtr<AddRuleAction> action = adoptRefWillBeNoop(new AddRuleAction(inspectorStyleSheet, selector)); 913 bool success = m_domAgent->history()->perform(action, exceptionState); 914 if (!success) { 915 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 916 return; 917 } 918 919 InspectorCSSId ruleId = action->newRuleId(); 920 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId); 921 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule)); 922 } 923 924 void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses) 925 { 926 Element* element = m_domAgent->assertElement(errorString, nodeId); 927 if (!element) 928 return; 929 930 unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get()); 931 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId); 932 unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value; 933 bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState; 934 if (!needStyleRecalc) 935 return; 936 937 if (forcedPseudoState) 938 m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState); 939 else 940 m_nodeIdToForcedPseudoState.remove(nodeId); 941 element->ownerDocument()->setNeedsStyleRecalc(SubtreeStyleChange); 942 } 943 944 PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet) 945 { 946 // Make certain compilers happy by initializing |source| up-front. 947 TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet; 948 switch (mediaListSource) { 949 case MediaListSourceMediaRule: 950 source = TypeBuilder::CSS::CSSMedia::Source::MediaRule; 951 break; 952 case MediaListSourceImportRule: 953 source = TypeBuilder::CSS::CSSMedia::Source::ImportRule; 954 break; 955 case MediaListSourceLinkedSheet: 956 source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet; 957 break; 958 case MediaListSourceInlineSheet: 959 source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet; 960 break; 961 } 962 963 const MediaQuerySet* queries = media->queries(); 964 const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queryVector = queries->queryVector(); 965 bool hasMediaQueryItems = false; 966 RefPtr<TypeBuilder::Array<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > > mediaListArray = TypeBuilder::Array<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> >::create(); 967 for (size_t i = 0; i < queryVector.size(); ++i) { 968 MediaQuery* query = queryVector.at(i).get(); 969 const ExpressionHeapVector& expressions = query->expressions(); 970 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > expressionArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression>::create(); 971 bool hasExpressionItems = false; 972 for (size_t j = 0; j < expressions.size(); ++j) { 973 MediaQueryExp* mediaQueryExp = expressions.at(j).get(); 974 MediaQueryExpValue expValue = mediaQueryExp->expValue(); 975 if (!expValue.isValue) 976 continue; 977 const char* valueName = CSSPrimitiveValue::unitTypeToString(expValue.unit); 978 RefPtr<TypeBuilder::CSS::MediaQueryExpression> mediaQueryExpression = TypeBuilder::CSS::MediaQueryExpression::create() 979 .setValue(expValue.value) 980 .setUnit(String(valueName)) 981 .setFeature(mediaQueryExp->mediaFeature()); 982 RefPtr<MediaValues> mediaValues = MediaValues::createDynamicIfFrameExists(parentStyleSheet->ownerDocument()->frame()); 983 int computedLength; 984 if (mediaValues->computeLength(expValue.value, expValue.unit, computedLength)) 985 mediaQueryExpression->setComputedLength(computedLength); 986 987 expressionArray->addItem(mediaQueryExpression); 988 hasExpressionItems = true; 989 } 990 if (hasExpressionItems) { 991 mediaListArray->addItem(expressionArray); 992 hasMediaQueryItems = true; 993 } 994 } 995 996 RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create() 997 .setText(media->mediaText()) 998 .setSource(source); 999 if (hasMediaQueryItems) 1000 mediaObject->setMediaList(mediaListArray); 1001 1002 if (parentStyleSheet && mediaListSource != MediaListSourceLinkedSheet) { 1003 if (InspectorStyleSheet* inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet)) 1004 mediaObject->setParentStyleSheetId(inspectorStyleSheet->id()); 1005 } 1006 1007 if (!sourceURL.isEmpty()) { 1008 mediaObject->setSourceURL(sourceURL); 1009 1010 CSSRule* parentRule = media->parentRule(); 1011 if (!parentRule) 1012 return mediaObject.release(); 1013 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet()); 1014 RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule); 1015 if (mediaRange) 1016 mediaObject->setRange(mediaRange); 1017 } 1018 return mediaObject.release(); 1019 } 1020 1021 bool InspectorCSSAgent::collectMediaQueriesFromStyleSheet(CSSStyleSheet* styleSheet, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray) 1022 { 1023 bool addedItems = false; 1024 MediaList* mediaList = styleSheet->media(); 1025 String sourceURL; 1026 if (mediaList && mediaList->length()) { 1027 Document* doc = styleSheet->ownerDocument(); 1028 if (doc) 1029 sourceURL = doc->url(); 1030 else if (!styleSheet->contents()->baseURL().isEmpty()) 1031 sourceURL = styleSheet->contents()->baseURL(); 1032 else 1033 sourceURL = ""; 1034 mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet)); 1035 addedItems = true; 1036 } 1037 return addedItems; 1038 } 1039 1040 bool InspectorCSSAgent::collectMediaQueriesFromRule(CSSRule* rule, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray) 1041 { 1042 MediaList* mediaList; 1043 String sourceURL; 1044 CSSStyleSheet* parentStyleSheet = 0; 1045 bool isMediaRule = true; 1046 bool addedItems = false; 1047 if (rule->type() == CSSRule::MEDIA_RULE) { 1048 CSSMediaRule* mediaRule = toCSSMediaRule(rule); 1049 mediaList = mediaRule->media(); 1050 parentStyleSheet = mediaRule->parentStyleSheet(); 1051 } else if (rule->type() == CSSRule::IMPORT_RULE) { 1052 CSSImportRule* importRule = toCSSImportRule(rule); 1053 mediaList = importRule->media(); 1054 parentStyleSheet = importRule->parentStyleSheet(); 1055 isMediaRule = false; 1056 } else { 1057 mediaList = 0; 1058 } 1059 1060 if (parentStyleSheet) { 1061 sourceURL = parentStyleSheet->contents()->baseURL(); 1062 if (sourceURL.isEmpty()) 1063 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument()); 1064 } else { 1065 sourceURL = ""; 1066 } 1067 1068 if (mediaList && mediaList->length()) { 1069 mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet)); 1070 addedItems = true; 1071 } 1072 return addedItems; 1073 } 1074 1075 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule) 1076 { 1077 if (!rule) 1078 return nullptr; 1079 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create(); 1080 bool hasItems = false; 1081 CSSRule* parentRule = rule; 1082 while (parentRule) { 1083 hasItems = collectMediaQueriesFromRule(parentRule, mediaArray.get()) || hasItems; 1084 if (parentRule->parentRule()) { 1085 parentRule = parentRule->parentRule(); 1086 } else { 1087 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet(); 1088 while (styleSheet) { 1089 hasItems = collectMediaQueriesFromStyleSheet(styleSheet, mediaArray.get()) || hasItems; 1090 parentRule = styleSheet->ownerRule(); 1091 if (parentRule) 1092 break; 1093 styleSheet = styleSheet->parentStyleSheet(); 1094 } 1095 } 1096 } 1097 return hasItems ? mediaArray : nullptr; 1098 } 1099 1100 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element) 1101 { 1102 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element); 1103 if (it != m_nodeToInspectorStyleSheet.end()) 1104 return it->value.get(); 1105 1106 CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0; 1107 if (!style) 1108 return 0; 1109 1110 String newStyleSheetId = String::number(m_lastStyleSheetId++); 1111 RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, this); 1112 m_idToInspectorStyleSheetForInlineStyle.set(newStyleSheetId, inspectorStyleSheet); 1113 m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet); 1114 return inspectorStyleSheet.get(); 1115 } 1116 1117 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId) 1118 { 1119 Node* node = m_domAgent->nodeForId(nodeId); 1120 if (!node) { 1121 *errorString = "No node with given id found"; 1122 return 0; 1123 } 1124 if (!node->isElementNode()) { 1125 *errorString = "Not an element node"; 1126 return 0; 1127 } 1128 return toElement(node); 1129 } 1130 1131 // static 1132 void InspectorCSSAgent::collectAllDocumentStyleSheets(Document* document, Vector<CSSStyleSheet*>& result) 1133 { 1134 const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets = document->styleEngine()->activeStyleSheetsForInspector(); 1135 for (WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >::const_iterator it = activeStyleSheets.begin(); it != activeStyleSheets.end(); ++it) { 1136 CSSStyleSheet* styleSheet = it->get(); 1137 InspectorCSSAgent::collectStyleSheets(styleSheet, result); 1138 } 1139 } 1140 1141 // static 1142 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, Vector<CSSStyleSheet*>& result) 1143 { 1144 result.append(styleSheet); 1145 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) { 1146 CSSRule* rule = styleSheet->item(i); 1147 if (rule->type() == CSSRule::IMPORT_RULE) { 1148 CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet(); 1149 if (importedStyleSheet) 1150 InspectorCSSAgent::collectStyleSheets(importedStyleSheet, result); 1151 } 1152 } 1153 } 1154 1155 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet) 1156 { 1157 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet); 1158 if (!inspectorStyleSheet) { 1159 String id = String::number(m_lastStyleSheetId++); 1160 Document* document = styleSheet->ownerDocument(); 1161 inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this); 1162 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet); 1163 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet); 1164 if (m_creatingViaInspectorStyleSheet) 1165 m_documentToViaInspectorStyleSheet.add(document, inspectorStyleSheet); 1166 } 1167 return inspectorStyleSheet.get(); 1168 } 1169 1170 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet) 1171 { 1172 String id = inspectorStyleSheet->id(); 1173 m_idToInspectorStyleSheet.remove(id); 1174 if (inspectorStyleSheet->pageStyleSheet()) 1175 m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet()); 1176 return id; 1177 } 1178 1179 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent) 1180 { 1181 if (!document) { 1182 ASSERT(!createIfAbsent); 1183 return 0; 1184 } 1185 1186 if (!document->isHTMLDocument() && !document->isSVGDocument()) 1187 return 0; 1188 1189 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToViaInspectorStyleSheet.get(document); 1190 if (inspectorStyleSheet || !createIfAbsent) 1191 return inspectorStyleSheet.get(); 1192 1193 TrackExceptionState exceptionState; 1194 RefPtrWillBeRawPtr<Element> styleElement = document->createElement("style", exceptionState); 1195 if (!exceptionState.hadException()) 1196 styleElement->setAttribute("type", "text/css", exceptionState); 1197 if (!exceptionState.hadException()) { 1198 ContainerNode* targetNode; 1199 // HEAD is absent in ImageDocuments, for example. 1200 if (document->head()) 1201 targetNode = document->head(); 1202 else if (document->body()) 1203 targetNode = document->body(); 1204 else 1205 return 0; 1206 1207 InlineStyleOverrideScope overrideScope(document); 1208 m_creatingViaInspectorStyleSheet = true; 1209 targetNode->appendChild(styleElement, exceptionState); 1210 // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation. 1211 // We just need to pick the respective InspectorStyleSheet from m_documentToViaInspectorStyleSheet. 1212 m_creatingViaInspectorStyleSheet = false; 1213 } 1214 if (exceptionState.hadException()) 1215 return 0; 1216 1217 return m_documentToViaInspectorStyleSheet.get(document); 1218 } 1219 1220 InspectorStyleSheet* InspectorCSSAgent::assertInspectorStyleSheetForId(ErrorString* errorString, const String& styleSheetId) 1221 { 1222 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId); 1223 if (it == m_idToInspectorStyleSheet.end()) { 1224 *errorString = "No style sheet with given id found"; 1225 return 0; 1226 } 1227 return it->value.get(); 1228 } 1229 1230 InspectorStyleSheetBase* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId) 1231 { 1232 String placeholder; 1233 InspectorStyleSheetBase* result = assertInspectorStyleSheetForId(&placeholder, styleSheetId); 1234 if (result) 1235 return result; 1236 IdToInspectorStyleSheetForInlineStyle::iterator it = m_idToInspectorStyleSheetForInlineStyle.find(styleSheetId); 1237 if (it == m_idToInspectorStyleSheetForInlineStyle.end()) { 1238 *errorString = "No style sheet with given id found"; 1239 return 0; 1240 } 1241 return it->value.get(); 1242 } 1243 1244 TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument) 1245 { 1246 if (m_creatingViaInspectorStyleSheet) 1247 return TypeBuilder::CSS::StyleSheetOrigin::Inspector; 1248 1249 TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular; 1250 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty()) 1251 origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent; 1252 else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->isDocumentNode()) 1253 origin = TypeBuilder::CSS::StyleSheetOrigin::User; 1254 else { 1255 InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false); 1256 if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet()) 1257 origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector; 1258 } 1259 return origin; 1260 } 1261 1262 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule) 1263 { 1264 if (!rule) 1265 return nullptr; 1266 1267 // CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent pointers if they are coming from 1268 // user agent stylesheets. To work around this issue, we use CSSOM wrapper created by inspector. 1269 if (!rule->parentStyleSheet()) { 1270 if (!m_inspectorUserAgentStyleSheet) 1271 m_inspectorUserAgentStyleSheet = CSSStyleSheet::create(CSSDefaultStyleSheets::instance().defaultStyleSheet()); 1272 rule->setParentStyleSheet(m_inspectorUserAgentStyleSheet.get()); 1273 } 1274 return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule)); 1275 } 1276 1277 static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId) 1278 { 1279 // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector." 1280 // As such, check the last selector in the tag history. 1281 for (; !selector->isLastInTagHistory(); ++selector) { } 1282 PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO; 1283 1284 // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements. 1285 // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too. 1286 return selectorPseudoId == elementPseudoId; 1287 } 1288 1289 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, Element* element) 1290 { 1291 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create(); 1292 if (!ruleList) 1293 return result.release(); 1294 1295 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { 1296 CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i)); 1297 RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule); 1298 if (!ruleObject) 1299 continue; 1300 RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create(); 1301 const CSSSelectorList& selectorList = rule->styleRule()->selectorList(); 1302 long index = 0; 1303 PseudoId elementPseudoId = element->pseudoId(); 1304 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) { 1305 const CSSSelector* firstTagHistorySelector = selector; 1306 bool matched = false; 1307 if (elementPseudoId) 1308 matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|. 1309 matched |= element->matches(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION); 1310 if (matched) 1311 matchingSelectors->addItem(index); 1312 ++index; 1313 } 1314 RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create() 1315 .setRule(ruleObject.release()) 1316 .setMatchingSelectors(matchingSelectors.release()); 1317 result->addItem(match); 1318 } 1319 1320 return result; 1321 } 1322 1323 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element) 1324 { 1325 if (!element->isStyledElement()) 1326 return nullptr; 1327 1328 // FIXME: Ugliness below. 1329 StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle()); 1330 if (!attributeStyle) 1331 return nullptr; 1332 1333 MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle); 1334 1335 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0); 1336 return inspectorStyle->buildObjectForStyle(); 1337 } 1338 1339 void InspectorCSSAgent::didRemoveDocument(Document* document) 1340 { 1341 if (document) 1342 m_documentToViaInspectorStyleSheet.remove(document); 1343 } 1344 1345 void InspectorCSSAgent::didRemoveDOMNode(Node* node) 1346 { 1347 if (!node) 1348 return; 1349 1350 int nodeId = m_domAgent->boundNodeId(node); 1351 if (nodeId) 1352 m_nodeIdToForcedPseudoState.remove(nodeId); 1353 1354 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node); 1355 if (it == m_nodeToInspectorStyleSheet.end()) 1356 return; 1357 1358 m_idToInspectorStyleSheetForInlineStyle.remove(it->value->id()); 1359 m_nodeToInspectorStyleSheet.remove(node); 1360 } 1361 1362 void InspectorCSSAgent::didModifyDOMAttr(Element* element) 1363 { 1364 if (!element) 1365 return; 1366 1367 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element); 1368 if (it == m_nodeToInspectorStyleSheet.end()) 1369 return; 1370 1371 it->value->didModifyElementAttribute(); 1372 } 1373 1374 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheetBase* styleSheet) 1375 { 1376 flushPendingFrontendMessages(); 1377 m_frontend->styleSheetChanged(styleSheet->id()); 1378 } 1379 1380 void InspectorCSSAgent::willReparseStyleSheet() 1381 { 1382 ASSERT(!m_isSettingStyleSheetText); 1383 m_isSettingStyleSheetText = true; 1384 } 1385 1386 void InspectorCSSAgent::didReparseStyleSheet() 1387 { 1388 ASSERT(m_isSettingStyleSheetText); 1389 m_isSettingStyleSheetText = false; 1390 } 1391 1392 void InspectorCSSAgent::resetPseudoStates() 1393 { 1394 HashSet<Document*> documentsToChange; 1395 for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) { 1396 Element* element = toElement(m_domAgent->nodeForId(it->key)); 1397 if (element && element->ownerDocument()) 1398 documentsToChange.add(element->ownerDocument()); 1399 } 1400 1401 m_nodeIdToForcedPseudoState.clear(); 1402 for (HashSet<Document*>::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it) 1403 (*it)->setNeedsStyleRecalc(SubtreeStyleChange); 1404 } 1405 1406 } // namespace WebCore 1407 1408