bool SVGLinearGradientElement::collectGradientAttributes(LinearGradientAttributes& attributes) { if (!renderer()) return false; WillBeHeapHashSet<RawPtrWillBeMember<SVGGradientElement> > processedGradients; SVGGradientElement* current = this; setGradientAttributes(current, attributes); processedGradients.add(current); while (true) { // Respect xlink:href, take attributes from referenced element Node* refNode = SVGURIReference::targetElementFromIRIString(current->href()->currentValue()->value(), treeScope()); if (refNode && isSVGGradientElement(*refNode)) { current = toSVGGradientElement(refNode); // Cycle detection if (processedGradients.contains(current)) return true; if (!current->renderer()) return false; setGradientAttributes(current, attributes, isSVGLinearGradientElement(*current)); processedGradients.add(current); } else { return true; } } ASSERT_NOT_REACHED(); return false; }
void ScriptRunner::executeScripts() { RefPtrWillBeRawPtr<Document> protect(m_document.get()); WillBeHeapDeque<RawPtrWillBeMember<ScriptLoader>> scriptLoaders; scriptLoaders.swap(m_scriptsToExecuteSoon); WillBeHeapHashSet<RawPtrWillBeMember<ScriptLoader>> inorderSet; while (!m_scriptsToExecuteInOrder.isEmpty() && m_scriptsToExecuteInOrder.first()->isReady()) { ScriptLoader* script = m_scriptsToExecuteInOrder.takeFirst(); inorderSet.add(script); scriptLoaders.append(script); } while (!scriptLoaders.isEmpty()) { scriptLoaders.takeFirst()->execute(); m_document->decrementLoadEventDelayCount(); if (yieldForHighPriorityWork()) break; } // If we have to yield, we must re-enqueue any scriptLoaders back onto the front of // m_scriptsToExecuteInOrder or m_scriptsToExecuteSoon depending on where the script // came from. // NOTE a yield followed by a notifyScriptReady(... ASYNC_EXECUTION) will result in that script executing // before any pre-existing ScriptsToExecuteInOrder. while (!scriptLoaders.isEmpty()) { ScriptLoader* script = scriptLoaders.takeLast(); if (inorderSet.contains(script)) m_scriptsToExecuteInOrder.prepend(script); else m_scriptsToExecuteSoon.prepend(script); } }
void findGoodTouchTargets(const IntRect& touchBox, LocalFrame* mainFrame, Vector<IntRect>& goodTargets, WillBeHeapVector<RawPtrWillBeMember<Node> >& highlightNodes) { goodTargets.clear(); int touchPointPadding = ceil(std::max(touchBox.width(), touchBox.height()) * 0.5); IntPoint touchPoint = touchBox.center(); IntPoint contentsPoint = mainFrame->view()->windowToContents(touchPoint); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint(contentsPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, IntSize(touchPointPadding, touchPointPadding)); const WillBeHeapListHashSet<RefPtrWillBeMember<Node> >& hitResults = result.rectBasedTestResult(); // Blacklist nodes that are container of disambiguated nodes. // It is not uncommon to have a clickable <div> that contains other clickable objects. // This heuristic avoids excessive disambiguation in that case. WillBeHeapHashSet<RawPtrWillBeMember<Node> > blackList; for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) { // Ignore any Nodes that can't be clicked on. RenderObject* renderer = it->get()->renderer(); if (!renderer || !it->get()->willRespondToMouseClickEvents()) continue; // Blacklist all of the Node's containers. for (RenderBlock* container = renderer->containingBlock(); container; container = container->containingBlock()) { Node* containerNode = container->node(); if (!containerNode) continue; if (!blackList.add(containerNode).isNewEntry) break; } } WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData> touchTargets; float bestScore = 0; for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) { for (Node* node = it->get(); node; node = node->parentNode()) { if (blackList.contains(node)) continue; if (node->isDocumentNode() || isHTMLHtmlElement(*node) || isHTMLBodyElement(*node)) break; if (node->willRespondToMouseClickEvents()) { TouchTargetData& targetData = touchTargets.add(node, TouchTargetData()).storedValue->value; targetData.windowBoundingBox = boundingBoxForEventNodes(node); targetData.score = scoreTouchTarget(touchPoint, touchPointPadding, targetData.windowBoundingBox); bestScore = std::max(bestScore, targetData.score); break; } } } for (WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData>::iterator it = touchTargets.begin(); it != touchTargets.end(); ++it) { // Currently the scoring function uses the overlap area with the fat point as the score. // We ignore the candidates that has less than 1/2 overlap (we consider not really ambiguous enough) than the best candidate to avoid excessive popups. if (it->value.score < bestScore * 0.5) continue; goodTargets.append(it->value.windowBoundingBox); highlightNodes.append(it->key); } }
void NodeSet::traversalSort() const { WillBeHeapHashSet<RawPtrWillBeMember<Node> > nodes; bool containsAttributeNodes = false; unsigned nodeCount = m_nodes.size(); ASSERT(nodeCount > 1); for (unsigned i = 0; i < nodeCount; ++i) { Node* node = m_nodes[i].get(); nodes.add(node); if (node->isAttributeNode()) containsAttributeNodes = true; } WillBeHeapVector<RefPtrWillBeMember<Node> > sortedNodes; sortedNodes.reserveInitialCapacity(nodeCount); for (Node* n = findRootNode(m_nodes.first().get()); n; n = NodeTraversal::next(*n)) { if (nodes.contains(n)) sortedNodes.append(n); if (!containsAttributeNodes || !n->isElementNode()) continue; Element* element = toElement(n); if (!element->hasAttributes()) continue; AttributeCollection attributes = element->attributes(); AttributeCollection::const_iterator end = attributes.end(); for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { RefPtrWillBeRawPtr<Attr> attr = element->attrIfExists(it->name()); if (attr && nodes.contains(attr.get())) sortedNodes.append(attr); } } ASSERT(sortedNodes.size() == nodeCount); const_cast<WillBeHeapVector<RefPtrWillBeMember<Node> >&>(m_nodes).swap(sortedNodes); }
void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) { ASSERT(button->type() == InputTypeNames::radio); ASSERT(m_members.contains(button)); bool wasValid = isValid(); if (button->isRequired()) { ++m_requiredCount; } else { ASSERT(m_requiredCount); --m_requiredCount; } if (wasValid != isValid()) setNeedsValidityCheckForAllButtons(); }
void RadioButtonGroup::updateCheckedState(HTMLInputElement* button) { ASSERT(button->isRadioButton()); ASSERT(m_members.contains(button)); bool wasValid = isValid(); if (button->checked()) { setCheckedButton(button); } else { if (m_checkedButton == button) m_checkedButton = nullptr; } if (wasValid != isValid()) setNeedsValidityCheckForAllButtons(); }
void NodeSet::traversalSort() const { WillBeHeapHashSet<RawPtrWillBeMember<Node>> nodes; bool containsAttributeNodes = false; unsigned nodeCount = m_nodes.size(); ASSERT(nodeCount > 1); for (unsigned i = 0; i < nodeCount; ++i) { Node* node = m_nodes[i].get(); nodes.add(node); if (node->isAttributeNode()) containsAttributeNodes = true; } WillBeHeapVector<RefPtrWillBeMember<Node>> sortedNodes; sortedNodes.reserveInitialCapacity(nodeCount); for (Node& n : NodeTraversal::startsAt(findRootNode(m_nodes.first().get()))) { if (nodes.contains(&n)) sortedNodes.append(&n); if (!containsAttributeNodes || !n.isElementNode()) continue; Element* element = toElement(&n); AttributeCollection attributes = element->attributes(); for (auto& attribute : attributes) { RefPtrWillBeRawPtr<Attr> attr = element->attrIfExists(attribute.name()); if (attr && nodes.contains(attr.get())) sortedNodes.append(attr); } } ASSERT(sortedNodes.size() == nodeCount); const_cast<WillBeHeapVector<RefPtrWillBeMember<Node>>&>(m_nodes).swap(sortedNodes); }
void RadioButtonGroup::updateCheckedState(HTMLInputElement* button) { ASSERT(button->type() == InputTypeNames::radio); ASSERT(m_members.contains(button)); bool wasValid = isValid(); if (button->checked()) { setCheckedButton(button); } else { if (m_checkedButton == button) m_checkedButton = nullptr; } if (wasValid != isValid()) setNeedsValidityCheckForAllButtons(); for (HTMLInputElement* const inputElement : m_members) { inputElement->pseudoStateChanged(CSSSelector::PseudoIndeterminate); } }
bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes) { if (!renderer()) return false; WillBeHeapHashSet<RawPtrWillBeMember<SVGGradientElement> > processedGradients; SVGGradientElement* current = this; setGradientAttributes(current, attributes); processedGradients.add(current); while (true) { // Respect xlink:href, take attributes from referenced element Node* refNode = SVGURIReference::targetElementFromIRIString(current->href()->currentValue()->value(), treeScope()); if (refNode && isSVGGradientElement(*refNode)) { current = toSVGGradientElement(refNode); // Cycle detection if (processedGradients.contains(current)) break; if (!current->renderer()) return false; setGradientAttributes(current, attributes, isSVGRadialGradientElement(*current)); processedGradients.add(current); } else { break; } } // Handle default values for fx/fy if (!attributes.hasFx()) attributes.setFx(attributes.cx()); if (!attributes.hasFy()) attributes.setFy(attributes.cy()); return true; }
void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const { WillBeHeapHashSet<RawPtrWillBeMember<const SVGPatternElement>> processedPatterns; const SVGPatternElement* current = this; while (true) { setPatternAttributes(current, attributes); processedPatterns.add(current); // Respect xlink:href, take attributes from referenced element Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefString(), treeScope()); // Only consider attached SVG pattern elements. if (!isSVGPatternElement(refNode) || !refNode->layoutObject()) break; current = toSVGPatternElement(refNode); // Cycle detection if (processedPatterns.contains(current)) break; } }
static void sortBlock(unsigned from, unsigned to, WillBeHeapVector<NodeSetVector>& parentMatrix, bool mayContainAttributeNodes) { // Should not call this function with less that two nodes to sort. ASSERT(from + 1 < to); unsigned minDepth = UINT_MAX; for (unsigned i = from; i < to; ++i) { unsigned depth = parentMatrix[i].size() - 1; if (minDepth > depth) minDepth = depth; } // Find the common ancestor. unsigned commonAncestorDepth = minDepth; Node* commonAncestor; while (true) { commonAncestor = parentWithDepth(commonAncestorDepth, parentMatrix[from]); if (commonAncestorDepth == 0) break; bool allEqual = true; for (unsigned i = from + 1; i < to; ++i) { if (commonAncestor != parentWithDepth(commonAncestorDepth, parentMatrix[i])) { allEqual = false; break; } } if (allEqual) break; --commonAncestorDepth; } if (commonAncestorDepth == minDepth) { // One of the nodes is the common ancestor => it is the first in // document order. Find it and move it to the beginning. for (unsigned i = from; i < to; ++i) { if (commonAncestor == parentMatrix[i][0]) { parentMatrix[i].swap(parentMatrix[from]); if (from + 2 < to) sortBlock(from + 1, to, parentMatrix, mayContainAttributeNodes); return; } } } if (mayContainAttributeNodes && commonAncestor->isElementNode()) { // The attribute nodes and namespace nodes of an element occur before // the children of the element. The namespace nodes are defined to occur // before the attribute nodes. The relative order of namespace nodes is // implementation-dependent. The relative order of attribute nodes is // implementation-dependent. unsigned sortedEnd = from; // FIXME: namespace nodes are not implemented. for (unsigned i = sortedEnd; i < to; ++i) { Node* n = parentMatrix[i][0]; if (n->isAttributeNode() && toAttr(n)->ownerElement() == commonAncestor) parentMatrix[i].swap(parentMatrix[sortedEnd++]); } if (sortedEnd != from) { if (to - sortedEnd > 1) sortBlock(sortedEnd, to, parentMatrix, mayContainAttributeNodes); return; } } // Children nodes of the common ancestor induce a subdivision of our // node-set. Sort it according to this subdivision, and recursively sort // each group. WillBeHeapHashSet<RawPtrWillBeMember<Node> > parentNodes; for (unsigned i = from; i < to; ++i) parentNodes.add(parentWithDepth(commonAncestorDepth + 1, parentMatrix[i])); unsigned previousGroupEnd = from; unsigned groupEnd = from; for (Node* n = commonAncestor->firstChild(); n; n = n->nextSibling()) { // If parentNodes contains the node, perform a linear search to move its // children in the node-set to the beginning. if (parentNodes.contains(n)) { for (unsigned i = groupEnd; i < to; ++i) { if (parentWithDepth(commonAncestorDepth + 1, parentMatrix[i]) == n) parentMatrix[i].swap(parentMatrix[groupEnd++]); } if (groupEnd - previousGroupEnd > 1) sortBlock(previousGroupEnd, groupEnd, parentMatrix, mayContainAttributeNodes); ASSERT(previousGroupEnd != groupEnd); previousGroupEnd = groupEnd; #ifndef NDEBUG parentNodes.remove(n); #endif } } ASSERT(parentNodes.isEmpty()); }
void TableSectionPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutRect localPaintInvalidationRect = paintInfo.rect; localPaintInvalidationRect.moveBy(-paintOffset); LayoutRect tableAlignedRect = m_renderTableSection.logicalRectForWritingModeAndDirection(localPaintInvalidationRect); CellSpan dirtiedRows = m_renderTableSection.dirtiedRows(tableAlignedRect); CellSpan dirtiedColumns = m_renderTableSection.dirtiedColumns(tableAlignedRect); WillBeHeapHashSet<RawPtrWillBeMember<RenderTableCell> > overflowingCells = m_renderTableSection.overflowingCells(); if (dirtiedColumns.start() < dirtiedColumns.end()) { if (!m_renderTableSection.hasMultipleCellLevels() && !overflowingCells.size()) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { // Collapsed borders are painted from the bottom right to the top left so that precedence // due to cell position is respected. for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) { unsigned row = r - 1; for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) { unsigned col = c - 1; RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(row, col); RenderTableCell* cell = current.primaryCell(); if (!cell || (row > dirtiedRows.start() && m_renderTableSection.primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(row, col - 1) == cell)) continue; LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cell, paintOffset); TableCellPainter(*cell).paintCollapsedBorders(paintInfo, cellPoint); } } } else { // Draw the dirty cells in the order that they appear. for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { RenderTableRow* row = m_renderTableSection.rowRendererAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c); RenderTableCell* cell = current.primaryCell(); if (!cell || (r > dirtiedRows.start() && m_renderTableSection.primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(r, c - 1) == cell)) continue; paintCell(cell, paintInfo, paintOffset); } } } } else { // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet. #if ENABLE(ASSERT) unsigned totalRows = m_renderTableSection.numRows(); unsigned totalCols = m_renderTableSection.table()->columns().size(); ASSERT(overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath); #endif // To make sure we properly paint invalidate the section, we paint invalidated all the overflowing cells that we collected. Vector<RenderTableCell*> cells; copyToVector(overflowingCells, cells); HashSet<RenderTableCell*> spanningCells; for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { RenderTableRow* row = m_renderTableSection.rowRendererAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c); if (!current.hasCells()) continue; for (unsigned i = 0; i < current.cells.size(); ++i) { if (overflowingCells.contains(current.cells[i])) continue; if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) { if (!spanningCells.add(current.cells[i]).isNewEntry) continue; } cells.append(current.cells[i]); } } } // Sort the dirty cells by paint order. if (!overflowingCells.size()) std::stable_sort(cells.begin(), cells.end(), compareCellPositions); else std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells); if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { for (unsigned i = cells.size(); i > 0; --i) { LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cells[i - 1], paintOffset); TableCellPainter(*cells[i - 1]).paintCollapsedBorders(paintInfo, cellPoint); } } else { for (unsigned i = 0; i < cells.size(); ++i) paintCell(cells[i], paintInfo, paintOffset); } } } }
bool RadioButtonGroup::contains(HTMLInputElement* button) const { return m_members.contains(button); }