bool HTMLEmbedElement::rendererIsNeeded(const NodeRenderingContext& context) { if (isImageType()) return HTMLPlugInImageElement::rendererIsNeeded(context); Frame* frame = document()->frame(); if (!frame) return false; // If my parent is an <object> and is not set to use fallback content, I // should be ignored and not get a renderer. ContainerNode* p = parentNode(); if (p && p->hasTagName(objectTag)) { ASSERT(p->renderer()); if (!static_cast<HTMLObjectElement*>(p)->useFallbackContent()) { ASSERT(!p->renderer()->isEmbeddedObject()); return false; } } #if ENABLE(DASHBOARD_SUPPORT) // Workaround for <rdar://problem/6642221>. if (Settings* settings = frame->settings()) { if (settings->usesDashboardBackwardCompatibilityMode()) return true; } #endif return HTMLPlugInImageElement::rendererIsNeeded(context); }
void Text::reattachIfNeeded(const AttachContext& context) { bool rendererIsNeeded = false; ContainerNode* renderingParent = NodeRenderingTraversal::parent(*this); if (renderingParent) { if (RenderObject* parentRenderer = renderingParent->renderer()) { if (textRendererIsNeeded(*parentRenderer->style(), *parentRenderer)) rendererIsNeeded = true; } } if (rendererIsNeeded == !!renderer()) return; // The following is almost the same as Node::reattach() except that we create renderer only if needed. // Not calling reattach() to avoid repeated calls to Text::textRendererIsNeeded(). AttachContext reattachContext(context); reattachContext.performingReattach = true; if (styleChangeType() < NeedsReattachStyleChange) detach(reattachContext); if (rendererIsNeeded) RenderTreeBuilderForText(*this, renderingParent->renderer()).createRenderer(); CharacterData::attach(reattachContext); }
bool HTMLEmbedElement::rendererIsNeeded(const RenderStyle& style) { if (isImageType()) return HTMLPlugInImageElement::rendererIsNeeded(style); // If my parent is an <object> and is not set to use fallback content, I // should be ignored and not get a renderer. ContainerNode* p = parentNode(); if (p && p->hasTagName(objectTag)) { if (!p->renderer()) return false; if (!toHTMLObjectElement(p)->useFallbackContent()) { ASSERT(!p->renderer()->isEmbeddedObject()); return false; } } #if ENABLE(DASHBOARD_SUPPORT) // Workaround for <rdar://problem/6642221>. if (document().frame()->settings().usesDashboardBackwardCompatibilityMode()) return true; #endif return HTMLPlugInImageElement::rendererIsNeeded(style); }
void SimplifyMarkupCommand::doApply() { ContainerNode* rootNode = m_firstNode->parentNode(); WillBeHeapVector<RefPtrWillBeMember<ContainerNode>> nodesToRemove; // Walk through the inserted nodes, to see if there are elements that could be removed // without affecting the style. The goal is to produce leaner markup even when starting // from a verbose fragment. // We look at inline elements as well as non top level divs that don't have attributes. for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = NodeTraversal::next(*node)) { if (node->hasChildren() || (node->isTextNode() && node->nextSibling())) continue; ContainerNode* startingNode = node->parentNode(); if (!startingNode) continue; RenderStyle* startingStyle = startingNode->renderStyle(); if (!startingStyle) continue; ContainerNode* currentNode = startingNode; ContainerNode* topNodeWithStartingStyle = nullptr; while (currentNode != rootNode) { if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode)) nodesToRemove.append(currentNode); currentNode = currentNode->parentNode(); if (!currentNode) break; if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes()) continue; if (currentNode->firstChild() != currentNode->lastChild()) { topNodeWithStartingStyle = 0; break; } if (!currentNode->renderStyle()->visualInvalidationDiff(*startingStyle).hasDifference()) topNodeWithStartingStyle = currentNode; } if (topNodeWithStartingStyle) { for (ContainerNode* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode()) nodesToRemove.append(node); } } // we perform all the DOM mutations at once. for (size_t i = 0; i < nodesToRemove.size(); ++i) { // FIXME: We can do better by directly moving children from nodesToRemove[i]. int numPrunedAncestors = pruneSubsequentAncestorsToRemove(nodesToRemove, i); if (numPrunedAncestors < 0) continue; removeNodePreservingChildren(nodesToRemove[i], AssumeContentIsAlwaysEditable); i += numPrunedAncestors; } }
void RemoveNodeCommand::doApply() { ContainerNode* parent = m_node->parentNode(); if (!parent || (m_shouldAssumeContentIsAlwaysEditable == DoNotAssumeContentIsAlwaysEditable && !parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) && parent->renderer())) return; ASSERT(parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) || !parent->renderer()); m_parent = parent; m_refChild = m_node->nextSibling(); m_node->remove(IGNORE_EXCEPTION); }
void SVGFELightElement::svgAttributeChanged(const QualifiedName& attrName) { SVGElement::svgAttributeChanged(attrName); if (attrName == SVGNames::azimuthAttr || attrName == SVGNames::elevationAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::zAttr || attrName == SVGNames::pointsAtXAttr || attrName == SVGNames::pointsAtYAttr || attrName == SVGNames::pointsAtZAttr || attrName == SVGNames::specularExponentAttr || attrName == SVGNames::limitingConeAngleAttr) { ContainerNode* parent = parentNode(); if (!parent) return; RenderObject* renderer = parent->renderer(); if (!renderer || !renderer->isSVGResourceFilterPrimitive()) return; if (parent->hasTagName(SVGNames::feDiffuseLightingTag)) { SVGFEDiffuseLightingElement* diffuseLighting = static_cast<SVGFEDiffuseLightingElement*>(parent); diffuseLighting->lightElementAttributeChanged(this, attrName); return; } // Handler for SpecularLighting has not implemented yet. RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } }
bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style) { if (!m_wasDemoted) return HTMLElement::rendererIsNeeded(style); ContainerNode* node = parentNode(); RenderObject* parentRenderer = node->renderer(); // FIXME: Shouldn't we also check for table caption (see |formIsTablePart| below). // FIXME: This check is not correct for Shadow DOM. bool parentIsTableElementPart = (parentRenderer->isTable() && isHTMLTableElement(node)) || (parentRenderer->isTableRow() && node->hasTagName(trTag)) || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag)) || (parentRenderer->isRenderTableCol() && node->hasTagName(colTag)) || (parentRenderer->isTableCell() && node->hasTagName(trTag)); if (!parentIsTableElementPart) return true; EDisplay display = style.display(); bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL || display == TABLE_CAPTION; return formIsTablePart; }
static void createRendererIfNeeded(Element& element, RenderStyle* resolvedStyle) { ASSERT(!element.renderer()); Document& document = element.document(); ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&element); RefPtr<RenderStyle> style = resolvedStyle; element.setIsInsideRegion(false); if (!shouldCreateRenderer(element, renderingParentNode) && !elementInsideRegionNeedsRenderer(element, renderingParentNode, style)) return; if (!style) style = element.styleForRenderer(); RenderNamedFlowThread* parentFlowRenderer = 0; #if ENABLE(CSS_REGIONS) parentFlowRenderer = moveToFlowThreadIfNeeded(element, *style); #endif if (!element.rendererIsNeeded(*style)) return; RenderObject* parentRenderer; RenderObject* nextRenderer; if (parentFlowRenderer) { parentRenderer = parentFlowRenderer; nextRenderer = parentFlowRenderer->nextRendererForNode(&element); } else { parentRenderer = renderingParentNode->renderer(); nextRenderer = nextSiblingRenderer(element, renderingParentNode); } RenderObject* newRenderer = element.createRenderer(document.renderArena(), style.get()); if (!newRenderer) return; if (!parentRenderer->isChildAllowed(newRenderer, style.get())) { newRenderer->destroy(); return; } // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. newRenderer->setFlowThreadState(parentRenderer->flowThreadState()); element.setRenderer(newRenderer); newRenderer->setAnimatableStyle(style.release()); // setAnimatableStyle() can depend on renderer() already being set. #if ENABLE(FULLSCREEN_API) if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) { newRenderer = RenderFullScreen::wrapRenderer(newRenderer, parentRenderer, &document); if (!newRenderer) return; } #endif // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer. parentRenderer->addChild(newRenderer, nextRenderer); }
void RenderTreeUpdater::updateRenderTree(ContainerNode& root) { ASSERT(root.renderer()); ASSERT(m_parentStack.isEmpty()); m_parentStack.append(Parent(root)); auto descendants = composedTreeDescendants(root); auto it = descendants.begin(); auto end = descendants.end(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156172 it.dropAssertions(); while (it != end) { popParentsToDepth(it.depth()); auto& node = *it; if (auto* renderer = node.renderer()) renderTreePosition().invalidateNextSibling(*renderer); if (is<Text>(node)) { auto& text = downcast<Text>(node); if (parent().styleChange == Style::Detach || m_styleUpdate->textUpdate(text) || m_invalidatedWhitespaceOnlyTextSiblings.contains(&text)) updateTextRenderer(text); it.traverseNextSkippingChildren(); continue; } auto& element = downcast<Element>(node); auto* elementUpdate = m_styleUpdate->elementUpdate(element); if (!elementUpdate) { it.traverseNextSkippingChildren(); continue; } updateElementRenderer(element, *elementUpdate); bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent())); if (!mayHaveRenderedDescendants) { it.traverseNextSkippingChildren(); continue; } pushParent(element, elementUpdate ? elementUpdate->change : Style::NoChange); it.traverseNext(); } popParentsToDepth(0); m_invalidatedWhitespaceOnlyTextSiblings.clear(); }
bool HTMLEmbedElement::rendererIsNeeded(const RenderStyle& style) { if (isImageType()) return HTMLPlugInImageElement::rendererIsNeeded(style); Frame* frame = document().frame(); if (!frame) return false; // If my parent is an <object> and is not set to use fallback content, I // should be ignored and not get a renderer. ContainerNode* p = parentNode(); if (p && p->hasTagName(objectTag)) { ASSERT(p->renderer()); if (!toHTMLObjectElement(p)->useFallbackContent()) { ASSERT(!p->renderer()->isEmbeddedObject()); return false; } } return HTMLPlugInImageElement::rendererIsNeeded(style); }
void SVGFELightElement::childrenChanged(const ChildChange& change) { SVGElement::childrenChanged(change); if (change.source == ChildChangeSourceParser) return; ContainerNode* parent = parentNode(); if (!parent) return; RenderElement* renderer = parent->renderer(); if (renderer && renderer->isSVGResourceFilterPrimitive()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); }
void invalidateFilterPrimitiveParent(SVGElement* element) { if (!element) return; ContainerNode* parent = element->parentNode(); if (!parent) return; RenderElement* renderer = parent->renderer(); if (!renderer || !renderer->isSVGResourceFilterPrimitive()) return; RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer, false); }
void SVGFEMergeNodeElement::svgAttributeChanged(const QualifiedName& attrName) { SVGElement::svgAttributeChanged(attrName); if (attrName != SVGNames::inAttr) return; ContainerNode* parentNode = parent(); if (!parentNode) return; RenderObject* renderer = parentNode->renderer(); if (!renderer || !renderer->isSVGResourceFilterPrimitive()) return; RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); }
static void createTextRendererIfNeeded(Text& textNode) { ASSERT(!textNode.renderer()); ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&textNode); if (!renderingParentNode) return; RenderObject* parentRenderer = renderingParentNode->renderer(); if (!parentRenderer || !parentRenderer->canHaveChildren()) return; if (!renderingParentNode->childShouldCreateRenderer(&textNode)) return; Document& document = textNode.document(); RefPtr<RenderStyle> style; bool resetStyleInheritance = textNode.parentNode()->isShadowRoot() && toShadowRoot(textNode.parentNode())->resetStyleInheritance(); if (resetStyleInheritance) style = document.ensureStyleResolver().defaultStyleForElement(); else style = parentRenderer->style(); if (!textRendererIsNeeded(textNode, *parentRenderer, *style)) return; RenderText* newRenderer = textNode.createTextRenderer(document.renderArena(), style.get()); if (!newRenderer) return; if (!parentRenderer->isChildAllowed(newRenderer, style.get())) { newRenderer->destroy(); return; } // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. newRenderer->setFlowThreadState(parentRenderer->flowThreadState()); RenderObject* nextRenderer = nextSiblingRenderer(textNode); textNode.setRenderer(newRenderer); // Parent takes care of the animations, no need to call setAnimatableStyle. newRenderer->setStyle(style.release()); parentRenderer->addChild(newRenderer, nextRenderer); Node* sibling = textNode.nextSibling(); if (sibling && !sibling->renderer() && sibling->attached()) createTextRenderersForSiblingsAfterAttachIfNeeded(sibling); }
void SVGFELightElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGElement::svgAttributeChanged(attrName); return; } SVGElementInstance::InvalidationGuard invalidationGuard(this); if (attrName == SVGNames::azimuthAttr || attrName == SVGNames::elevationAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::zAttr || attrName == SVGNames::pointsAtXAttr || attrName == SVGNames::pointsAtYAttr || attrName == SVGNames::pointsAtZAttr || attrName == SVGNames::specularExponentAttr || attrName == SVGNames::limitingConeAngleAttr) { ContainerNode* parent = parentNode(); if (!parent) return; RenderObject* renderer = parent->renderer(); if (!renderer || !renderer->isSVGResourceFilterPrimitive()) return; if (parent->hasTagName(SVGNames::feDiffuseLightingTag)) { SVGFEDiffuseLightingElement* diffuseLighting = static_cast<SVGFEDiffuseLightingElement*>(parent); diffuseLighting->lightElementAttributeChanged(this, attrName); return; } else if (parent->hasTagName(SVGNames::feSpecularLightingTag)) { SVGFESpecularLightingElement* specularLighting = static_cast<SVGFESpecularLightingElement*>(parent); specularLighting->lightElementAttributeChanged(this, attrName); return; } } ASSERT_NOT_REACHED(); }
bool HTMLFormElement::rendererIsNeeded(RenderStyle* style) { if (!isDemoted()) return HTMLElement::rendererIsNeeded(style); ContainerNode* node = parentNode(); RenderObject* parentRenderer = node->renderer(); bool parentIsTableElementPart = (parentRenderer->isTable() && node->hasTagName(tableTag)) || (parentRenderer->isTableRow() && node->hasTagName(trTag)) || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag)) || (parentRenderer->isTableCol() && node->hasTagName(colTag)) || (parentRenderer->isTableCell() && node->hasTagName(trTag)); if (!parentIsTableElementPart) return true; EDisplay display = style->display(); bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL || display == TABLE_CAPTION; return formIsTablePart; }
static bool isDeletableElement(const Node* node) { if (!node || !node->isHTMLElement() || !node->inDocument() || !node->rendererIsEditable()) return false; // In general we want to only draw the UI around object of a certain area, but we still keep the min width/height to // make sure we don't end up with very thin or very short elements getting the UI. const int minimumArea = 2500; const int minimumWidth = 48; const int minimumHeight = 16; const unsigned minimumVisibleBorders = 1; RenderObject* renderer = node->renderer(); if (!renderer || !renderer->isBox()) return false; // Disallow the body element since it isn't practical to delete, and the deletion UI would be clipped. if (node->hasTagName(bodyTag)) return false; // Disallow elements with any overflow clip, since the deletion UI would be clipped as well. <rdar://problem/6840161> if (renderer->hasOverflowClip()) return false; // Disallow Mail blockquotes since the deletion UI would get in the way of editing for these. if (isMailBlockquote(node)) return false; RenderBox* box = toRenderBox(renderer); IntRect borderBoundingBox = box->borderBoundingBox(); if (borderBoundingBox.width() < minimumWidth || borderBoundingBox.height() < minimumHeight) return false; if ((borderBoundingBox.width() * borderBoundingBox.height()) < minimumArea) return false; if (renderer->isTable()) return true; if (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(iframeTag)) return true; if (renderer->isOutOfFlowPositioned()) return true; if (renderer->isRenderBlock() && !renderer->isTableCell()) { RenderStyle* style = renderer->style(); if (!style) return false; // Allow blocks that have background images if (style->hasBackgroundImage()) { for (const FillLayer* background = style->backgroundLayers(); background; background = background->next()) { if (background->image() && background->image()->canRender(renderer, 1)) return true; } } // Allow blocks with a minimum number of non-transparent borders unsigned visibleBorders = style->borderTop().isVisible() + style->borderBottom().isVisible() + style->borderLeft().isVisible() + style->borderRight().isVisible(); if (visibleBorders >= minimumVisibleBorders) return true; // Allow blocks that have a different background from it's parent ContainerNode* parentNode = node->parentNode(); if (!parentNode) return false; RenderObject* parentRenderer = parentNode->renderer(); if (!parentRenderer) return false; RenderStyle* parentStyle = parentRenderer->style(); if (!parentStyle) return false; if (renderer->hasBackground() && (!parentRenderer->hasBackground() || style->visitedDependentColor(CSSPropertyBackgroundColor) != parentStyle->visitedDependentColor(CSSPropertyBackgroundColor))) return true; } return false; }
Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode) { const bool shouldEmit = traversalMode == EmitString; Vector<Node*> ancestorsToClose; Node* next; Node* lastClosed = 0; for (Node* n = startNode; n != pastEnd; n = next) { // According to <rdar://problem/5730668>, it is possible for n to blow // past pastEnd and become null here. This shouldn't be possible. // This null check will prevent crashes (but create too much markup) // and the ASSERT will hopefully lead us to understanding the problem. ASSERT(n); if (!n) break; next = NodeTraversal::next(n); bool openedTag = false; if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd) // Don't write out empty block containers that aren't fully selected. continue; if (!n->renderer() && !enclosingNodeWithTag(firstPositionInOrBeforeNode(n), selectTag)) { next = NodeTraversal::nextSkippingChildren(n); // Don't skip over pastEnd. if (pastEnd && pastEnd->isDescendantOf(n)) next = pastEnd; } else { // Add the node to the markup if we're not skipping the descendants if (shouldEmit) appendStartTag(n); // If node has no children, close the tag now. if (!n->childNodeCount()) { if (shouldEmit) appendEndTag(n); lastClosed = n; } else { openedTag = true; ancestorsToClose.append(n); } } // If we didn't insert open tag and there's no more siblings or we're at the end of the traversal, take care of ancestors. // FIXME: What happens if we just inserted open tag and reached the end? if (!openedTag && (!n->nextSibling() || next == pastEnd)) { // Close up the ancestors. while (!ancestorsToClose.isEmpty()) { Node* ancestor = ancestorsToClose.last(); if (next != pastEnd && next->isDescendantOf(ancestor)) break; // Not at the end of the range, close ancestors up to sibling of next node. if (shouldEmit) appendEndTag(ancestor); lastClosed = ancestor; ancestorsToClose.removeLast(); } // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors. ContainerNode* nextParent = next ? next->parentNode() : 0; if (next != pastEnd && n != nextParent) { Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n; for (ContainerNode* parent = lastAncestorClosedOrSelf->parentNode(); parent && parent != nextParent; parent = parent->parentNode()) { // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered: if (!parent->renderer()) continue; // or b) ancestors that we never encountered during a pre-order traversal starting at startNode: ASSERT(startNode->isDescendantOf(parent)); if (shouldEmit) wrapWithNode(parent); lastClosed = parent; } } } } return lastClosed; }
RenderTreeUpdater::Parent::Parent(ContainerNode& root) : element(is<Document>(root) ? nullptr : downcast<Element>(&root)) , renderTreePosition(RenderTreePosition(*root.renderer())) { }