1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2001 Peter Kelly (pmk@post.com) 5 * (C) 2001 Dirk Mueller (mueller@kde.org) 6 * (C) 2007 David Smith (catfish.man@gmail.com) 7 * Copyright (C) 2004-2010, 2012-2014 Apple Inc. All rights reserved. 8 * (C) 2007 Eric Seidel (eric@webkit.org) 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Library General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Library General Public License for more details. 19 * 20 * You should have received a copy of the GNU Library General Public License 21 * along with this library; see the file COPYING.LIB. If not, write to 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 * Boston, MA 02110-1301, USA. 24 */ 25 26#include "config.h" 27#include "StyleResolveTree.h" 28 29#include "AXObjectCache.h" 30#include "AnimationController.h" 31#include "CSSFontSelector.h" 32#include "ElementIterator.h" 33#include "ElementRareData.h" 34#include "FlowThreadController.h" 35#include "InsertionPoint.h" 36#include "LoaderStrategy.h" 37#include "MainFrame.h" 38#include "NodeRenderStyle.h" 39#include "NodeRenderingTraversal.h" 40#include "NodeTraversal.h" 41#include "PlatformStrategies.h" 42#include "RenderFullScreen.h" 43#include "RenderNamedFlowThread.h" 44#include "RenderText.h" 45#include "RenderView.h" 46#include "RenderWidget.h" 47#include "ResourceLoadScheduler.h" 48#include "Settings.h" 49#include "ShadowRoot.h" 50#include "StyleResolveForDocument.h" 51#include "StyleResolver.h" 52#include "Text.h" 53 54#if PLATFORM(IOS) 55#include "CSSFontSelector.h" 56#include "WKContentObservation.h" 57#endif 58 59namespace WebCore { 60 61namespace Style { 62 63enum DetachType { NormalDetach, ReattachDetach }; 64 65class RenderTreePosition { 66public: 67 RenderTreePosition(RenderElement* parent); 68 RenderTreePosition(RenderElement* parent, RenderObject* nextSibling); 69 70 RenderElement* parent() { return m_parent; } 71 72 void insert(RenderObject&); 73 bool canInsert(RenderElement&) const; 74 bool canInsert(RenderText&) const; 75 76 void computeNextSibling(const Node&, ContainerNode& renderingParentNode); 77 void invalidateNextSibling(const RenderObject&); 78 79private: 80 RenderElement* m_parent; 81 RenderObject* m_nextSibling; 82 bool m_hasValidNextSibling; 83#if !ASSERT_DISABLED 84 unsigned m_assertionLimitCounter; 85#endif 86}; 87 88static void attachRenderTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, PassRefPtr<RenderStyle>); 89static void attachTextRenderer(Text&, ContainerNode& renderingParentNode, RenderTreePosition&); 90static void detachRenderTree(Element&, DetachType); 91static void resolveTextNode(Text&, ContainerNode& renderingParentNode, RenderTreePosition&); 92static void resolveTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, Change); 93 94Change determineChange(const RenderStyle* s1, const RenderStyle* s2) 95{ 96 if (!s1 || !s2) 97 return Detach; 98 if (s1->display() != s2->display()) 99 return Detach; 100 if (s1->hasPseudoStyle(FIRST_LETTER) != s2->hasPseudoStyle(FIRST_LETTER)) 101 return Detach; 102 // We just detach if a renderer acquires or loses a column-span, since spanning elements 103 // typically won't contain much content. 104 if (s1->columnSpan() != s2->columnSpan()) 105 return Detach; 106 if (!s1->contentDataEquivalent(s2)) 107 return Detach; 108 // When text-combine property has been changed, we need to prepare a separate renderer object. 109 // When text-combine is on, we use RenderCombineText, otherwise RenderText. 110 // https://bugs.webkit.org/show_bug.cgi?id=55069 111 if (s1->hasTextCombine() != s2->hasTextCombine()) 112 return Detach; 113 // We need to reattach the node, so that it is moved to the correct RenderFlowThread. 114 if (s1->flowThread() != s2->flowThread()) 115 return Detach; 116 // When the region thread has changed, we need to prepare a separate render region object. 117 if (s1->regionThread() != s2->regionThread()) 118 return Detach; 119 120 if (*s1 != *s2) { 121 if (s1->inheritedNotEqual(s2)) 122 return Inherit; 123 if (s1->hasExplicitlyInheritedProperties() || s2->hasExplicitlyInheritedProperties()) 124 return Inherit; 125 126 return NoInherit; 127 } 128 // If the pseudoStyles have changed, we want any StyleChange that is not NoChange 129 // because setStyle will do the right thing with anything else. 130 if (s1->hasAnyPublicPseudoStyles()) { 131 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < FIRST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { 132 if (s1->hasPseudoStyle(pseudoId)) { 133 RenderStyle* ps2 = s2->getCachedPseudoStyle(pseudoId); 134 if (!ps2) 135 return NoInherit; 136 RenderStyle* ps1 = s1->getCachedPseudoStyle(pseudoId); 137 if (!ps1 || *ps1 != *ps2) 138 return NoInherit; 139 } 140 } 141 } 142 143 return NoChange; 144} 145 146static bool isRendererReparented(const RenderObject* renderer) 147{ 148 if (!renderer->node()->isElementNode()) 149 return false; 150 if (renderer->style().hasFlowInto()) 151 return true; 152 return false; 153} 154 155static RenderObject* nextSiblingRenderer(const Node& node, const ContainerNode& renderingParentNode) 156{ 157 if (!renderingParentNode.isElementNode()) 158 return nullptr; 159 const Element& renderingParentElement = toElement(renderingParentNode); 160 // Avoid an O(N^2) problem with this function by not checking for 161 // nextRenderer() when the parent element hasn't attached yet. 162 // FIXME: Why would we get here anyway if parent is not attached? 163 if (!renderingParentElement.renderer()) 164 return nullptr; 165 if (node.isAfterPseudoElement()) 166 return nullptr; 167 Node* sibling = node.isBeforePseudoElement() ? NodeRenderingTraversal::firstChild(&renderingParentNode) : NodeRenderingTraversal::nextSibling(&node); 168 for (; sibling; sibling = NodeRenderingTraversal::nextSibling(sibling)) { 169 RenderObject* renderer = sibling->renderer(); 170 if (renderer && !isRendererReparented(renderer)) 171 return renderer; 172 } 173 if (PseudoElement* after = renderingParentElement.afterPseudoElement()) 174 return after->renderer(); 175 return nullptr; 176} 177 178RenderTreePosition::RenderTreePosition(RenderElement* parent) 179 : m_parent(parent) 180 , m_nextSibling(nullptr) 181 , m_hasValidNextSibling(false) 182#if !ASSERT_DISABLED 183 , m_assertionLimitCounter(0) 184#endif 185{ 186} 187 188RenderTreePosition::RenderTreePosition(RenderElement* parent, RenderObject* nextSibling) 189 : m_parent(parent) 190 , m_nextSibling(nextSibling) 191 , m_hasValidNextSibling(true) 192#if !ASSERT_DISABLED 193 , m_assertionLimitCounter(0) 194#endif 195{ 196} 197 198bool RenderTreePosition::canInsert(RenderElement& renderer) const 199{ 200 ASSERT(m_parent); 201 ASSERT(!renderer.parent()); 202 return m_parent->isChildAllowed(renderer, renderer.style()); 203} 204 205bool RenderTreePosition::canInsert(RenderText& renderer) const 206{ 207 ASSERT(m_parent); 208 ASSERT(!renderer.parent()); 209 return m_parent->isChildAllowed(renderer, m_parent->style()); 210} 211 212void RenderTreePosition::insert(RenderObject& renderer) 213{ 214 ASSERT(m_parent); 215 ASSERT(m_hasValidNextSibling); 216 m_parent->addChild(&renderer, m_nextSibling); 217} 218 219void RenderTreePosition::computeNextSibling(const Node& node, ContainerNode& renderingParentNode) 220{ 221 ASSERT(!node.renderer()); 222 if (m_hasValidNextSibling) { 223 // Stop validating at some point so the assert doesn't make us O(N^2) on debug builds. 224 ASSERT(++m_assertionLimitCounter > 20 || nextSiblingRenderer(node, renderingParentNode) == m_nextSibling); 225 return; 226 } 227 m_nextSibling = nextSiblingRenderer(node, renderingParentNode); 228 m_hasValidNextSibling = true; 229} 230 231void RenderTreePosition::invalidateNextSibling(const RenderObject& siblingRenderer) 232{ 233 if (!m_hasValidNextSibling) 234 return; 235 if (m_nextSibling == &siblingRenderer) 236 m_hasValidNextSibling = false; 237} 238 239static bool shouldCreateRenderer(const Element& element, const ContainerNode& renderingParent) 240{ 241 if (!element.document().shouldCreateRenderers()) 242 return false; 243 RenderObject* parentRenderer = renderingParent.renderer(); 244 if (!parentRenderer) 245 return false; 246 if (!parentRenderer->canHaveChildren() && !(element.isPseudoElement() && parentRenderer->canHaveGeneratedChildren())) 247 return false; 248 if (!renderingParent.childShouldCreateRenderer(element)) 249 return false; 250 return true; 251} 252 253static PassRef<RenderStyle> styleForElement(Element& element, ContainerNode& renderingParentNode) 254{ 255 RenderStyle* parentStyle = renderingParentNode.renderStyle(); 256 if (element.hasCustomStyleResolveCallbacks() && parentStyle) { 257 if (RefPtr<RenderStyle> style = element.customStyleForRenderer(*parentStyle)) 258 return style.releaseNonNull(); 259 } 260 return element.document().ensureStyleResolver().styleForElement(&element, parentStyle); 261} 262 263#if ENABLE(CSS_REGIONS) 264static RenderNamedFlowThread* moveToFlowThreadIfNeeded(Element& element, const RenderStyle& style) 265{ 266 if (!element.shouldMoveToFlowThread(style)) 267 return 0; 268 269 FlowThreadController& flowThreadController = element.document().renderView()->flowThreadController(); 270 RenderNamedFlowThread& parentFlowRenderer = flowThreadController.ensureRenderFlowThreadWithName(style.flowThread()); 271 flowThreadController.registerNamedFlowContentElement(element, parentFlowRenderer); 272 return &parentFlowRenderer; 273} 274#endif 275 276static void createRendererIfNeeded(Element& element, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle) 277{ 278 ASSERT(!element.renderer()); 279 280 RefPtr<RenderStyle> style = resolvedStyle; 281 282 if (!shouldCreateRenderer(element, renderingParentNode)) 283 return; 284 285 if (!style) 286 style = styleForElement(element, renderingParentNode); 287 288 RenderNamedFlowThread* parentFlowRenderer = 0; 289#if ENABLE(CSS_REGIONS) 290 parentFlowRenderer = moveToFlowThreadIfNeeded(element, *style); 291#endif 292 293 if (!element.rendererIsNeeded(*style)) 294 return; 295 296 renderTreePosition.computeNextSibling(element, renderingParentNode); 297 298 RenderTreePosition insertionPosition = parentFlowRenderer 299 ? RenderTreePosition(parentFlowRenderer, parentFlowRenderer->nextRendererForElement(element)) 300 : renderTreePosition; 301 302 RenderElement* newRenderer = element.createElementRenderer(style.releaseNonNull()).leakPtr(); 303 if (!newRenderer) 304 return; 305 if (!insertionPosition.canInsert(*newRenderer)) { 306 newRenderer->destroy(); 307 return; 308 } 309 310 // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style 311 // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. 312 newRenderer->setFlowThreadState(insertionPosition.parent()->flowThreadState()); 313 314 // Code below updateAnimations() can depend on Element::renderer() already being set. 315 element.setRenderer(newRenderer); 316 317 // FIXME: There's probably a better way to factor this. 318 // This just does what setAnimatedStyle() does, except with setStyleInternal() instead of setStyle(). 319 newRenderer->setStyleInternal(newRenderer->animation().updateAnimations(*newRenderer, newRenderer->style())); 320 321 newRenderer->initializeStyle(); 322 323#if ENABLE(FULLSCREEN_API) 324 Document& document = element.document(); 325 if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) { 326 newRenderer = RenderFullScreen::wrapRenderer(newRenderer, insertionPosition.parent(), document); 327 if (!newRenderer) 328 return; 329 } 330#endif 331 // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer. 332 insertionPosition.insert(*newRenderer); 333} 334 335static RenderObject* previousSiblingRenderer(const Text& textNode) 336{ 337 if (textNode.renderer()) 338 return textNode.renderer()->previousSibling(); 339 for (Node* sibling = NodeRenderingTraversal::previousSibling(&textNode); sibling; sibling = NodeRenderingTraversal::previousSibling(sibling)) { 340 RenderObject* renderer = sibling->renderer(); 341 if (renderer && !isRendererReparented(renderer)) 342 return renderer; 343 } 344 if (PseudoElement* before = textNode.parentElement()->beforePseudoElement()) 345 return before->renderer(); 346 return nullptr; 347} 348 349static void invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node& current) 350{ 351 if (isInsertionPoint(current)) 352 return; 353 // This function finds sibling text renderers where the results of textRendererIsNeeded may have changed as a result of 354 // the current node gaining or losing the renderer. This can only affect white space text nodes. 355 for (Node* sibling = current.nextSibling(); sibling; sibling = sibling->nextSibling()) { 356 if (sibling->needsStyleRecalc()) 357 return; 358 if (sibling->isElementNode()) { 359 // Text renderers beyond rendered elements can't be affected. 360 if (!sibling->renderer() || isRendererReparented(sibling->renderer())) 361 continue; 362 return; 363 } 364 if (!sibling->isTextNode()) 365 continue; 366 Text& textSibling = toText(*sibling); 367 if (!textSibling.containsOnlyWhitespace()) 368 continue; 369 textSibling.setNeedsStyleRecalc(); 370 } 371} 372 373static bool textRendererIsNeeded(const Text& textNode, ContainerNode& renderingParentNode) 374{ 375 if (!renderingParentNode.renderer()) 376 return false; 377 RenderElement& parentRenderer = *renderingParentNode.renderer(); 378 if (!parentRenderer.canHaveChildren()) 379 return false; 380 if (!renderingParentNode.childShouldCreateRenderer(textNode)) 381 return false; 382 383 if (textNode.isEditingText()) 384 return true; 385 if (!textNode.length()) 386 return false; 387 if (parentRenderer.style().display() == NONE) 388 return false; 389 if (!textNode.containsOnlyWhitespace()) 390 return true; 391 // This text node has nothing but white space. We may still need a renderer in some cases. 392 if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet()) 393 return false; 394 if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 395 return true; 396 397 RenderObject* previousRenderer = previousSiblingRenderer(textNode); 398 if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span> 399 return false; 400 401 if (parentRenderer.isRenderInline()) { 402 // <span><div/> <div/></span> 403 if (previousRenderer && !previousRenderer->isInline()) 404 return false; 405 } else { 406 if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline())) 407 return false; 408 409 RenderObject* first = parentRenderer.firstChild(); 410 while (first && first->isFloatingOrOutOfFlowPositioned()) 411 first = first->nextSibling(); 412 RenderObject* nextRenderer = nextSiblingRenderer(textNode, *textNode.parentNode()); 413 if (!first || nextRenderer == first) { 414 // Whitespace at the start of a block just goes away. Don't even make a render object for this text. 415 return false; 416 } 417 } 418 return true; 419} 420 421static void createTextRendererIfNeeded(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition) 422{ 423 ASSERT(!textNode.renderer()); 424 425 if (!textRendererIsNeeded(textNode, renderingParentNode)) 426 return; 427 428 auto newRenderer = textNode.createTextRenderer(*renderingParentNode.renderStyle()); 429 ASSERT(newRenderer); 430 431 renderTreePosition.computeNextSibling(textNode, renderingParentNode); 432 433 if (!renderTreePosition.canInsert(*newRenderer)) 434 return; 435 436 // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style 437 // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. 438 newRenderer->setFlowThreadState(renderTreePosition.parent()->flowThreadState()); 439 440 textNode.setRenderer(newRenderer.get()); 441 // Parent takes care of the animations, no need to call setAnimatableStyle. 442 renderTreePosition.insert(*newRenderer.leakPtr()); 443} 444 445void attachTextRenderer(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition) 446{ 447 createTextRendererIfNeeded(textNode, renderingParentNode, renderTreePosition); 448 449 textNode.clearNeedsStyleRecalc(); 450} 451 452void detachTextRenderer(Text& textNode) 453{ 454 if (textNode.renderer()) 455 textNode.renderer()->destroyAndCleanupAnonymousWrappers(); 456 textNode.setRenderer(0); 457} 458 459void updateTextRendererAfterContentChange(Text& textNode, unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) 460{ 461 ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&textNode); 462 if (!renderingParentNode) 463 return; 464 465 bool hadRenderer = textNode.renderer(); 466 467 RenderTreePosition renderTreePosition(renderingParentNode->renderer()); 468 resolveTextNode(textNode, *renderingParentNode, renderTreePosition); 469 470 if (hadRenderer && textNode.renderer()) 471 textNode.renderer()->setTextWithOffset(textNode.dataImpl(), offsetOfReplacedData, lengthOfReplacedData); 472} 473 474static void attachChildren(ContainerNode& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition) 475{ 476 for (Node* child = current.firstChild(); child; child = child->nextSibling()) { 477 ASSERT((!child->renderer() || child->isNamedFlowContentNode()) || current.shadowRoot()); 478 if (child->renderer()) { 479 renderTreePosition.invalidateNextSibling(*child->renderer()); 480 continue; 481 } 482 if (child->isTextNode()) { 483 attachTextRenderer(*toText(child), renderingParentNode, renderTreePosition); 484 continue; 485 } 486 if (child->isElementNode()) 487 attachRenderTree(*toElement(child), renderingParentNode, renderTreePosition, nullptr); 488 } 489} 490 491static void attachDistributedChildren(InsertionPoint& insertionPoint, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition) 492{ 493 if (ShadowRoot* shadowRoot = insertionPoint.containingShadowRoot()) 494 ContentDistributor::ensureDistribution(shadowRoot); 495 496 for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) { 497 if (current->renderer()) 498 renderTreePosition.invalidateNextSibling(*current->renderer()); 499 if (current->isTextNode()) { 500 if (current->renderer()) 501 continue; 502 attachTextRenderer(*toText(current), renderingParentNode, renderTreePosition); 503 continue; 504 } 505 if (current->isElementNode()) { 506 Element& currentElement = toElement(*current); 507 if (currentElement.renderer()) 508 detachRenderTree(currentElement); 509 attachRenderTree(currentElement, renderingParentNode, renderTreePosition, nullptr); 510 } 511 } 512 // Use actual children as fallback content. 513 if (!insertionPoint.hasDistribution()) 514 attachChildren(insertionPoint, renderingParentNode, renderTreePosition); 515} 516 517static void attachShadowRoot(ShadowRoot& shadowRoot) 518{ 519 ASSERT(shadowRoot.hostElement()); 520 521 RenderTreePosition renderTreePosition(shadowRoot.hostElement()->renderer()); 522 attachChildren(shadowRoot, *shadowRoot.hostElement(), renderTreePosition); 523 524 shadowRoot.clearNeedsStyleRecalc(); 525 shadowRoot.clearChildNeedsStyleRecalc(); 526} 527 528static PseudoElement* beforeOrAfterPseudoElement(Element& current, PseudoId pseudoId) 529{ 530 ASSERT(pseudoId == BEFORE || pseudoId == AFTER); 531 if (pseudoId == BEFORE) 532 return current.beforePseudoElement(); 533 return current.afterPseudoElement(); 534} 535 536static void setBeforeOrAfterPseudoElement(Element& current, PassRefPtr<PseudoElement> pseudoElement, PseudoId pseudoId) 537{ 538 ASSERT(pseudoId == BEFORE || pseudoId == AFTER); 539 if (pseudoId == BEFORE) { 540 current.setBeforePseudoElement(pseudoElement); 541 return; 542 } 543 current.setAfterPseudoElement(pseudoElement); 544} 545 546static void clearBeforeOrAfterPseudoElement(Element& current, PseudoId pseudoId) 547{ 548 ASSERT(pseudoId == BEFORE || pseudoId == AFTER); 549 if (pseudoId == BEFORE) { 550 current.clearBeforePseudoElement(); 551 return; 552 } 553 current.clearAfterPseudoElement(); 554} 555 556static bool needsPseudoElement(Element& current, PseudoId pseudoId) 557{ 558 if (!current.document().styleSheetCollection().usesBeforeAfterRules()) 559 return false; 560 if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren()) 561 return false; 562 if (current.isPseudoElement()) 563 return false; 564 if (!pseudoElementRendererIsNeeded(current.renderer()->getCachedPseudoStyle(pseudoId))) 565 return false; 566 return true; 567} 568 569static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId, RenderTreePosition& renderTreePosition) 570{ 571 if (!needsPseudoElement(current, pseudoId)) 572 return; 573 RefPtr<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId); 574 setBeforeOrAfterPseudoElement(current, pseudoElement, pseudoId); 575 attachRenderTree(*pseudoElement, current, renderTreePosition, nullptr); 576} 577 578static void attachRenderTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle) 579{ 580 PostResolutionCallbackDisabler callbackDisabler(current.document()); 581 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; 582 583 if (current.hasCustomStyleResolveCallbacks()) 584 current.willAttachRenderers(); 585 586 createRendererIfNeeded(current, renderingParentNode, renderTreePosition, resolvedStyle); 587 588 if (current.parentElement() && current.parentElement()->isInCanvasSubtree()) 589 current.setIsInCanvasSubtree(true); 590 591 StyleResolverParentPusher parentPusher(¤t); 592 593 RenderTreePosition childRenderTreePosition(current.renderer()); 594 attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE, childRenderTreePosition); 595 596 if (ShadowRoot* shadowRoot = current.shadowRoot()) { 597 parentPusher.push(); 598 attachShadowRoot(*shadowRoot); 599 } else if (current.firstChild()) 600 parentPusher.push(); 601 602 if (isInsertionPoint(current)) 603 attachDistributedChildren(toInsertionPoint(current), renderingParentNode, renderTreePosition); 604 else 605 attachChildren(current, current, childRenderTreePosition); 606 607 current.clearNeedsStyleRecalc(); 608 current.clearChildNeedsStyleRecalc(); 609 610 if (AXObjectCache* cache = current.document().axObjectCache()) 611 cache->updateCacheAfterNodeIsAttached(¤t); 612 613 attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER, childRenderTreePosition); 614 615 current.updateFocusAppearanceAfterAttachIfNeeded(); 616 617 if (current.hasCustomStyleResolveCallbacks()) 618 current.didAttachRenderers(); 619} 620 621static void detachDistributedChildren(InsertionPoint& insertionPoint) 622{ 623 for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) { 624 if (current->isTextNode()) { 625 detachTextRenderer(*toText(current)); 626 continue; 627 } 628 if (current->isElementNode()) 629 detachRenderTree(*toElement(current)); 630 } 631} 632 633static void detachChildren(ContainerNode& current, DetachType detachType) 634{ 635 if (isInsertionPoint(current)) 636 detachDistributedChildren(toInsertionPoint(current)); 637 638 for (Node* child = current.firstChild(); child; child = child->nextSibling()) { 639 if (child->isTextNode()) { 640 Style::detachTextRenderer(*toText(child)); 641 continue; 642 } 643 if (child->isElementNode()) 644 detachRenderTree(*toElement(child), detachType); 645 } 646 current.clearChildNeedsStyleRecalc(); 647} 648 649static void detachShadowRoot(ShadowRoot& shadowRoot, DetachType detachType) 650{ 651 detachChildren(shadowRoot, detachType); 652} 653 654static void detachRenderTree(Element& current, DetachType detachType) 655{ 656 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; 657 658 if (current.hasCustomStyleResolveCallbacks()) 659 current.willDetachRenderers(); 660 661 current.clearStyleDerivedDataBeforeDetachingRenderer(); 662 663 // Do not remove the element's hovered and active status 664 // if performing a reattach. 665 if (detachType != ReattachDetach) 666 current.clearHoverAndActiveStatusBeforeDetachingRenderer(); 667 668 if (ShadowRoot* shadowRoot = current.shadowRoot()) 669 detachShadowRoot(*shadowRoot, detachType); 670 671 detachChildren(current, detachType); 672 673 if (current.renderer()) 674 current.renderer()->destroyAndCleanupAnonymousWrappers(); 675 current.setRenderer(0); 676 677 if (current.hasCustomStyleResolveCallbacks()) 678 current.didDetachRenderers(); 679} 680 681static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newStyle) 682{ 683 const RenderStyle& currentStyle = renderer->style(); 684 685 const PseudoStyleCache* pseudoStyleCache = currentStyle.cachedPseudoStyles(); 686 if (!pseudoStyleCache) 687 return false; 688 689 size_t cacheSize = pseudoStyleCache->size(); 690 for (size_t i = 0; i < cacheSize; ++i) { 691 RefPtr<RenderStyle> newPseudoStyle; 692 PseudoId pseudoId = pseudoStyleCache->at(i)->styleType(); 693 if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) 694 newPseudoStyle = renderer->uncachedFirstLineStyle(newStyle); 695 else 696 newPseudoStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle); 697 if (!newPseudoStyle) 698 return true; 699 if (*newPseudoStyle != *pseudoStyleCache->at(i)) { 700 if (pseudoId < FIRST_INTERNAL_PSEUDOID) 701 newStyle->setHasPseudoStyle(pseudoId); 702 newStyle->addCachedPseudoStyle(newPseudoStyle); 703 if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) { 704 // FIXME: We should do an actual diff to determine whether a repaint vs. layout 705 // is needed, but for now just assume a layout will be required. The diff code 706 // in RenderObject::setStyle would need to be factored out so that it could be reused. 707 renderer->setNeedsLayoutAndPrefWidthsRecalc(); 708 } 709 return true; 710 } 711 } 712 return false; 713} 714 715static Change resolveLocal(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change inheritedChange) 716{ 717 Change localChange = Detach; 718 RefPtr<RenderStyle> newStyle; 719 RefPtr<RenderStyle> currentStyle = current.renderStyle(); 720 721 Document& document = current.document(); 722 if (currentStyle && current.styleChangeType() != ReconstructRenderTree) { 723 newStyle = styleForElement(current, renderingParentNode); 724 localChange = determineChange(currentStyle.get(), newStyle.get()); 725 } 726 if (localChange == Detach) { 727 if (current.renderer() || current.isNamedFlowContentNode()) 728 detachRenderTree(current, ReattachDetach); 729 attachRenderTree(current, renderingParentNode, renderTreePosition, newStyle.release()); 730 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(current); 731 732 return Detach; 733 } 734 735 if (RenderElement* renderer = current.renderer()) { 736 if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (inheritedChange == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange) 737 renderer->setAnimatableStyle(*newStyle); 738 else if (current.needsStyleRecalc()) { 739 // Although no change occurred, we use the new style so that the cousin style sharing code won't get 740 // fooled into believing this style is the same. 741 renderer->setStyleInternal(*newStyle); 742 } 743 } 744 745 // If "rem" units are used anywhere in the document, and if the document element's font size changes, then go ahead and force font updating 746 // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway). 747 if (document.styleSheetCollection().usesRemUnits() && document.documentElement() == ¤t && localChange != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize()) { 748 // Cached RenderStyles may depend on the re units. 749 if (StyleResolver* styleResolver = document.styleResolverIfExists()) 750 styleResolver->invalidateMatchedPropertiesCache(); 751 return Force; 752 } 753 if (inheritedChange == Force) 754 return Force; 755 if (current.styleChangeType() >= FullStyleChange) 756 return Force; 757 758 return localChange; 759} 760 761void resolveTextNode(Text& text, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition) 762{ 763 text.clearNeedsStyleRecalc(); 764 765 bool hasRenderer = text.renderer(); 766 bool needsRenderer = textRendererIsNeeded(text, renderingParentNode); 767 if (hasRenderer) { 768 if (needsRenderer) 769 return; 770 detachTextRenderer(text); 771 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text); 772 return; 773 } 774 if (!needsRenderer) 775 return; 776 attachTextRenderer(text, renderingParentNode, renderTreePosition); 777 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text); 778} 779 780static void resolveShadowTree(ShadowRoot& shadowRoot, Element& host, Style::Change change) 781{ 782 ASSERT(shadowRoot.hostElement() == &host); 783 RenderTreePosition renderTreePosition(host.renderer()); 784 for (Node* child = shadowRoot.firstChild(); child; child = child->nextSibling()) { 785 if (child->renderer()) 786 renderTreePosition.invalidateNextSibling(*child->renderer()); 787 if (child->isTextNode() && child->needsStyleRecalc()) { 788 resolveTextNode(*toText(child), host, renderTreePosition); 789 continue; 790 } 791 if (child->isElementNode()) 792 resolveTree(*toElement(child), host, renderTreePosition, change); 793 } 794 795 shadowRoot.clearNeedsStyleRecalc(); 796 shadowRoot.clearChildNeedsStyleRecalc(); 797} 798 799static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition) 800{ 801 if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) { 802 if (existingPseudoElement->renderer()) 803 renderTreePosition.invalidateNextSibling(*existingPseudoElement->renderer()); 804 805 if (needsPseudoElement(current, pseudoId)) 806 resolveTree(*existingPseudoElement, current, renderTreePosition, current.needsStyleRecalc() ? Force : change); 807 else 808 clearBeforeOrAfterPseudoElement(current, pseudoId); 809 return; 810 } 811 attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId, renderTreePosition); 812} 813 814#if PLATFORM(IOS) 815static EVisibility elementImplicitVisibility(const Element* element) 816{ 817 RenderObject* renderer = element->renderer(); 818 if (!renderer) 819 return VISIBLE; 820 821 RenderStyle& style = renderer->style(); 822 823 Length width(style.width()); 824 Length height(style.height()); 825 if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0)) 826 return HIDDEN; 827 828 Length top(style.top()); 829 Length left(style.left()); 830 if (left.isFixed() && width.isFixed() && -left.value() >= width.value()) 831 return HIDDEN; 832 833 if (top.isFixed() && height.isFixed() && -top.value() >= height.value()) 834 return HIDDEN; 835 return VISIBLE; 836} 837 838class CheckForVisibilityChangeOnRecalcStyle { 839public: 840 CheckForVisibilityChangeOnRecalcStyle(Element* element, RenderStyle* currentStyle) 841 : m_element(element) 842 , m_previousDisplay(currentStyle ? currentStyle->display() : NONE) 843 , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN) 844 , m_previousImplicitVisibility(WKObservingContentChanges() && WKContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE) 845 { 846 } 847 ~CheckForVisibilityChangeOnRecalcStyle() 848 { 849 if (!WKObservingContentChanges()) 850 return; 851 RenderStyle* style = m_element->renderStyle(); 852 if (!style) 853 return; 854 if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN) 855 || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE)) 856 WKSetObservedContentChange(WKContentVisibilityChange); 857 } 858private: 859 RefPtr<Element> m_element; 860 EDisplay m_previousDisplay; 861 EVisibility m_previousVisibility; 862 EVisibility m_previousImplicitVisibility; 863}; 864#endif // PLATFORM(IOS) 865 866void resolveTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change change) 867{ 868 ASSERT(change != Detach); 869 870 if (current.hasCustomStyleResolveCallbacks()) { 871 if (!current.willRecalcStyle(change)) 872 return; 873 } 874 875 bool hasParentStyle = renderingParentNode.renderStyle(); 876 bool hasDirectAdjacentRules = current.childrenAffectedByDirectAdjacentRules(); 877 bool hasIndirectAdjacentRules = current.childrenAffectedByForwardPositionalRules(); 878 879#if PLATFORM(IOS) 880 CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(¤t, current.renderStyle()); 881#endif 882 883 if (change > NoChange || current.needsStyleRecalc()) 884 current.resetComputedStyle(); 885 886 if (hasParentStyle && (change >= Inherit || current.needsStyleRecalc())) 887 change = resolveLocal(current, renderingParentNode, renderTreePosition, change); 888 889 if (change != Detach) { 890 StyleResolverParentPusher parentPusher(¤t); 891 892 if (ShadowRoot* shadowRoot = current.shadowRoot()) { 893 if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) { 894 parentPusher.push(); 895 resolveShadowTree(*shadowRoot, current, change); 896 } 897 } 898 899 RenderTreePosition childRenderTreePosition(current.renderer()); 900 updateBeforeOrAfterPseudoElement(current, change, BEFORE, childRenderTreePosition); 901 902 // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. 903 // For now we will just worry about the common case, since it's a lot trickier to get the second case right 904 // without doing way too much re-resolution. 905 bool forceCheckOfNextElementSibling = false; 906 bool forceCheckOfAnyElementSibling = false; 907 for (Node* child = current.firstChild(); child; child = child->nextSibling()) { 908 if (child->renderer()) 909 childRenderTreePosition.invalidateNextSibling(*child->renderer()); 910 if (child->isTextNode() && child->needsStyleRecalc()) { 911 resolveTextNode(*toText(child), current, childRenderTreePosition); 912 continue; 913 } 914 if (!child->isElementNode()) 915 continue; 916 Element* childElement = toElement(child); 917 bool childRulesChanged = childElement->needsStyleRecalc() && childElement->styleChangeType() == FullStyleChange; 918 if ((forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)) 919 childElement->setNeedsStyleRecalc(); 920 if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) { 921 parentPusher.push(); 922 resolveTree(*childElement, current, childRenderTreePosition, change); 923 } 924 forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; 925 forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); 926 } 927 928 updateBeforeOrAfterPseudoElement(current, change, AFTER, childRenderTreePosition); 929 } 930 931 current.clearNeedsStyleRecalc(); 932 current.clearChildNeedsStyleRecalc(); 933 934 if (current.hasCustomStyleResolveCallbacks()) 935 current.didRecalcStyle(change); 936} 937 938void resolveTree(Document& document, Change change) 939{ 940 if (change == Force) { 941 auto documentStyle = resolveForDocument(document); 942 943 // Inserting the pictograph font at the end of the font fallback list is done by the 944 // font selector, so set a font selector if needed. 945 if (Settings* settings = document.settings()) { 946 StyleResolver* styleResolver = document.styleResolverIfExists(); 947 if (settings->fontFallbackPrefersPictographs() && styleResolver) 948 documentStyle.get().font().update(styleResolver->fontSelector()); 949 } 950 951 Style::Change documentChange = determineChange(&documentStyle.get(), &document.renderView()->style()); 952 if (documentChange != NoChange) 953 document.renderView()->setStyle(WTF::move(documentStyle)); 954 else 955 documentStyle.dropRef(); 956 } 957 958 Element* documentElement = document.documentElement(); 959 if (!documentElement) 960 return; 961 if (change < Inherit && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) 962 return; 963 RenderTreePosition renderTreePosition(document.renderView()); 964 resolveTree(*documentElement, document, renderTreePosition, change); 965} 966 967void detachRenderTree(Element& element) 968{ 969 detachRenderTree(element, NormalDetach); 970} 971 972static Vector<std::function<void ()>>& postResolutionCallbackQueue() 973{ 974 static NeverDestroyed<Vector<std::function<void ()>>> vector; 975 return vector; 976} 977 978void queuePostResolutionCallback(std::function<void ()> callback) 979{ 980 postResolutionCallbackQueue().append(callback); 981} 982 983static void suspendMemoryCacheClientCalls(Document& document) 984{ 985 Page* page = document.page(); 986 if (!page || !page->areMemoryCacheClientCallsEnabled()) 987 return; 988 989 page->setMemoryCacheClientCallsEnabled(false); 990 991 RefPtr<MainFrame> protectedMainFrame = &page->mainFrame(); 992 postResolutionCallbackQueue().append([protectedMainFrame]{ 993 if (Page* page = protectedMainFrame->page()) 994 page->setMemoryCacheClientCallsEnabled(true); 995 }); 996} 997 998static unsigned resolutionNestingDepth; 999 1000PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document) 1001{ 1002 ++resolutionNestingDepth; 1003 1004 if (resolutionNestingDepth == 1) 1005 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->suspendPendingRequests(); 1006 1007 // FIXME: It's strange to build this into the disabler. 1008 suspendMemoryCacheClientCalls(document); 1009} 1010 1011PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler() 1012{ 1013 if (resolutionNestingDepth == 1) { 1014 // Get size each time through the loop because a callback can add more callbacks to the end of the queue. 1015 auto& queue = postResolutionCallbackQueue(); 1016 for (size_t i = 0; i < queue.size(); ++i) 1017 queue[i](); 1018 queue.clear(); 1019 1020 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->resumePendingRequests(); 1021 } 1022 1023 --resolutionNestingDepth; 1024} 1025 1026bool postResolutionCallbacksAreSuspended() 1027{ 1028 return resolutionNestingDepth; 1029} 1030 1031} 1032} 1033