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