void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate& update, const Element* animatingElement, double timelineCurrentTime)
{
    ElementAnimations* elementAnimations = animatingElement ? animatingElement->elementAnimations() : nullptr;
    AnimationStack* animationStack = elementAnimations ? &elementAnimations->defaultStack() : nullptr;

    ActiveInterpolationsMap activeInterpolationsForTransitions;
    if (update.newTransitions().isEmpty() && update.cancelledTransitions().isEmpty()) {
        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, KeyframeEffect::TransitionPriority, timelineCurrentTime);
    } else {
        HeapVector<Member<InertEffect>> newTransitions;
        for (const auto& entry : update.newTransitions())
            newTransitions.append(entry.value.effect.get());

        HeapHashSet<Member<const Animation>> cancelledAnimations;
        if (!update.cancelledTransitions().isEmpty()) {
            ASSERT(elementAnimations);
            const TransitionMap& transitionMap = elementAnimations->cssAnimations().m_transitions;
            for (CSSPropertyID id : update.cancelledTransitions()) {
                ASSERT(transitionMap.contains(id));
                cancelledAnimations.add(transitionMap.get(id).animation.get());
            }
        }

        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimations, KeyframeEffect::TransitionPriority, timelineCurrentTime);
    }

    // Properties being animated by animations don't get values from transitions applied.
    if (!update.activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
        for (const auto& entry : update.activeInterpolationsForAnimations())
            activeInterpolationsForTransitions.remove(entry.key);
    }
    update.adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
}
Example #2
0
void NodeSet::traversalSort() const {
  HeapHashSet<Member<Node>> nodes;
  bool containsAttributeNodes = false;

  unsigned nodeCount = m_nodes.size();
  DCHECK_GT(nodeCount, 1u);
  for (unsigned i = 0; i < nodeCount; ++i) {
    Node* node = m_nodes[i].get();
    nodes.add(node);
    if (node->isAttributeNode())
      containsAttributeNodes = true;
  }

  HeapVector<Member<Node>> sortedNodes;
  sortedNodes.reserveInitialCapacity(nodeCount);

  for (Node& n : NodeTraversal::startsAt(*findRootNode(m_nodes.first()))) {
    if (nodes.contains(&n))
      sortedNodes.append(&n);

    if (!containsAttributeNodes || !n.isElementNode())
      continue;

    Element* element = toElement(&n);
    AttributeCollection attributes = element->attributes();
    for (auto& attribute : attributes) {
      Attr* attr = element->attrIfExists(attribute.name());
      if (attr && nodes.contains(attr))
        sortedNodes.append(attr);
    }
  }

  DCHECK_EQ(sortedNodes.size(), nodeCount);
  const_cast<HeapVector<Member<Node>>&>(m_nodes).swap(sortedNodes);
}
void V8MutationObserver::visitDOMWrapper(v8::Isolate* isolate, ScriptWrappable* scriptWrappable, const v8::Persistent<v8::Object>& wrapper)
{
    MutationObserver* observer = scriptWrappable->toImpl<MutationObserver>();
    HeapHashSet<Member<Node>> observedNodes = observer->getObservedNodes();
    for (HeapHashSet<Member<Node>>::iterator it = observedNodes.begin(); it != observedNodes.end(); ++it) {
        v8::UniqueId id(reinterpret_cast<intptr_t>(V8GCController::opaqueRootForGC(isolate, *it)));
        isolate->SetReferenceFromGroup(id, wrapper);
    }
}
Example #4
0
TEST_F(AnimationEffectStackTest, CancelledAnimations) {
  HeapHashSet<Member<const Animation>> cancelledAnimations;
  Animation* animation =
      play(makeKeyframeEffect(makeEffectModel(CSSPropertyFontSize,
                                              AnimatableDouble::create(1))),
           0);
  cancelledAnimations.add(animation);
  play(makeKeyframeEffect(
           makeEffectModel(CSSPropertyZIndex, AnimatableDouble::create(2))),
       0);
  ActiveInterpolationsMap result = EffectStack::activeInterpolations(
      &element->elementAnimations()->effectStack(), 0, &cancelledAnimations,
      KeyframeEffectReadOnly::DefaultPriority);
  EXPECT_EQ(1u, result.size());
  EXPECT_TRUE(interpolationValue(result, CSSPropertyZIndex)
                  ->equals(AnimatableDouble::create(2).get()));
}
Example #5
0
std::unique_ptr<MessagePortChannelArray> MessagePort::disentanglePorts(
    ExecutionContext* context,
    const MessagePortArray& ports,
    ExceptionState& exceptionState) {
  if (!ports.size())
    return nullptr;

  HeapHashSet<Member<MessagePort>> visited;

  // Walk the incoming array - if there are any duplicate ports, or null ports
  // or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec).
  for (unsigned i = 0; i < ports.size(); ++i) {
    MessagePort* port = ports[i];
    if (!port || port->isNeutered() || visited.contains(port)) {
      String type;
      if (!port)
        type = "null";
      else if (port->isNeutered())
        type = "already neutered";
      else
        type = "a duplicate";
      exceptionState.throwDOMException(
          DataCloneError,
          "Port at index " + String::number(i) + " is " + type + ".");
      return nullptr;
    }
    visited.add(port);
  }

  UseCounter::count(context, UseCounter::MessagePortsTransferred);

  // Passed-in ports passed validity checks, so we can disentangle them.
  std::unique_ptr<MessagePortChannelArray> portArray =
      wrapUnique(new MessagePortChannelArray(ports.size()));
  for (unsigned i = 0; i < ports.size(); ++i)
    (*portArray)[i] = ports[i]->disentangle();
  return portArray;
}
void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const
{
    HeapHashSet<Member<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;
    }
}
void ScopedStyleResolver::collectFeaturesTo(
    RuleFeatureSet& features,
    HeapHashSet<Member<const StyleSheetContents>>&
        visitedSharedStyleSheetContents) const {
  for (size_t i = 0; i < m_authorStyleSheets.size(); ++i) {
    ASSERT(m_authorStyleSheets[i]->ownerNode());
    StyleSheetContents* contents = m_authorStyleSheets[i]->contents();
    if (contents->hasOneClient() ||
        visitedSharedStyleSheetContents.add(contents).isNewEntry)
      features.add(contents->ruleSet().features());
  }

  if (!m_treeBoundaryCrossingRuleSet)
    return;

  for (const auto& rules : *m_treeBoundaryCrossingRuleSet)
    features.add(rules->m_ruleSet->features());
}
Example #8
0
void AXTable::addChildren()
{
    ASSERT(!isDetached());
    if (!isAXTable()) {
        AXLayoutObject::addChildren();
        return;
    }

    ASSERT(!m_haveChildren);

    m_haveChildren = true;
    if (!m_layoutObject || !m_layoutObject->isTable())
        return;

    LayoutTable* table = toLayoutTable(m_layoutObject);
    AXObjectCacheImpl& axCache = axObjectCache();

    Node* tableNode = table->node();
    if (!isHTMLTableElement(tableNode))
        return;

    // Add caption
    if (HTMLTableCaptionElement* caption  = toHTMLTableElement(tableNode)->caption()) {
        AXObject* captionObject = axCache.getOrCreate(caption);
        if (captionObject && !captionObject->accessibilityIsIgnored())
            m_children.append(captionObject);
    }

    // Go through all the available sections to pull out the rows and add them as children.
    table->recalcSectionsIfNeeded();
    LayoutTableSection* tableSection = table->topSection();
    if (!tableSection)
        return;

    LayoutTableSection* initialTableSection = tableSection;
    while (tableSection) {

        HeapHashSet<Member<AXObject>> appendedRows;
        unsigned numRows = tableSection->numRows();
        for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {

            LayoutTableRow* layoutRow = tableSection->rowLayoutObjectAt(rowIndex);
            if (!layoutRow)
                continue;

            AXObject* rowObject = axCache.getOrCreate(layoutRow);
            if (!rowObject || !rowObject->isTableRow())
                continue;

            AXTableRow* row = toAXTableRow(rowObject);
            // We need to check every cell for a new row, because cell spans
            // can cause us to miss rows if we just check the first column.
            if (appendedRows.contains(row))
                continue;

            row->setRowIndex(static_cast<int>(m_rows.size()));
            m_rows.append(row);
            if (!row->accessibilityIsIgnored())
                m_children.append(row);
            appendedRows.add(row);
        }

        tableSection = table->sectionBelow(tableSection, SkipEmptySections);
    }

    // make the columns based on the number of columns in the first body
    unsigned length = initialTableSection->numEffectiveColumns();
    for (unsigned i = 0; i < length; ++i) {
        AXTableColumn* column = toAXTableColumn(axCache.getOrCreate(ColumnRole));
        column->setColumnIndex((int)i);
        column->setParent(this);
        m_columns.append(column);
        if (!column->accessibilityIsIgnored())
            m_children.append(column);
    }

    AXObject* headerContainerObject = headerContainer();
    if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
        m_children.append(headerContainerObject);
}
Example #9
0
static void sortBlock(unsigned from,
                      unsigned to,
                      HeapVector<NodeSetVector>& parentMatrix,
                      bool mayContainAttributeNodes) {
  // Should not call this function with less that two nodes to sort.
  DCHECK_LT(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.
  HeapHashSet<Member<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);

      DCHECK_NE(previousGroupEnd, groupEnd);
      previousGroupEnd = groupEnd;
#if DCHECK_IS_ON()
      parentNodes.remove(n);
#endif
    }
  }

  DCHECK(parentNodes.isEmpty());
}
SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime)
{
    ASSERT(document().isActive());
    SMILTime earliestFireTime = SMILTime::unresolved();

#if ENABLE(ASSERT)
    // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section.
    // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply.
    m_preventScheduledAnimationsChanges = true;
#endif

    if (m_documentOrderIndexesDirty)
        updateDocumentOrderIndexes();

    HeapHashSet<ElementAttributePair> invalidKeys;
    using AnimationsVector = HeapVector<Member<SVGSMILElement>>;
    AnimationsVector animationsToApply;
    AnimationsVector scheduledAnimationsInSameGroup;
    for (const auto& entry : m_scheduledAnimations) {
        if (!entry.key.first || entry.value->isEmpty()) {
            invalidKeys.add(entry.key);
            continue;
        }

        // Sort according to priority. Elements with later begin time have higher priority.
        // In case of a tie, document order decides.
        // FIXME: This should also consider timing relationships between the elements. Dependents
        // have higher priority.
        copyToVector(*entry.value, scheduledAnimationsInSameGroup);
        std::sort(scheduledAnimationsInSameGroup.begin(), scheduledAnimationsInSameGroup.end(), PriorityCompare(elapsed));

        SVGSMILElement* resultElement = nullptr;
        for (const auto& itAnimation : scheduledAnimationsInSameGroup) {
            SVGSMILElement* animation = itAnimation.get();
            ASSERT(animation->timeContainer() == this);
            ASSERT(animation->targetElement());
            ASSERT(animation->hasValidAttributeName());
            ASSERT(animation->hasValidAttributeType());

            // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair.
            if (!resultElement) {
                resultElement = animation;
                resultElement->lockAnimatedType();
            }

            // This will calculate the contribution from the animation and add it to the resultElement.
            if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) {
                resultElement->unlockAnimatedType();
                resultElement->clearAnimatedType();
                resultElement = nullptr;
            }

            SMILTime nextFireTime = animation->nextProgressTime();
            if (nextFireTime.isFinite())
                earliestFireTime = std::min(nextFireTime, earliestFireTime);
        }

        if (resultElement) {
            animationsToApply.append(resultElement);
            resultElement->unlockAnimatedType();
        }
    }
    m_scheduledAnimations.removeAll(invalidKeys);

    std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed));

    unsigned animationsToApplySize = animationsToApply.size();
    if (!animationsToApplySize) {
#if ENABLE(ASSERT)
        m_preventScheduledAnimationsChanges = false;
#endif
        return earliestFireTime;
    }

    // Apply results to target elements.
    for (unsigned i = 0; i < animationsToApplySize; ++i)
        animationsToApply[i]->applyResultsToTarget();

#if ENABLE(ASSERT)
    m_preventScheduledAnimationsChanges = false;
#endif

    for (unsigned i = 0; i < animationsToApplySize; ++i) {
        if (animationsToApply[i]->inShadowIncludingDocument() && animationsToApply[i]->isSVGDiscardElement()) {
            SVGSMILElement* animDiscard = animationsToApply[i];
            SVGElement* targetElement = animDiscard->targetElement();
            if (targetElement && targetElement->inShadowIncludingDocument()) {
                targetElement->remove(IGNORE_EXCEPTION);
                ASSERT(!targetElement->inShadowIncludingDocument());
            }

            if (animDiscard->inShadowIncludingDocument()) {
                animDiscard->remove(IGNORE_EXCEPTION);
                ASSERT(!animDiscard->inShadowIncludingDocument());
            }
        }
    }
    return earliestFireTime;
}