void VTTCue::markFutureAndPastNodes(ContainerNode* root, double previousTimestamp, double movieTime) { DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp")); bool isPastNode = true; double currentTimestamp = previousTimestamp; if (currentTimestamp > movieTime) isPastNode = false; for (Node& child : NodeTraversal::descendantsOf(*root)) { if (child.nodeName() == timestampTag) { double currentTimestamp; bool check = VTTParser::collectTimeStamp(child.nodeValue(), currentTimestamp); ASSERT_UNUSED(check, check); if (currentTimestamp > movieTime) isPastNode = false; } if (child.isVTTElement()) { toVTTElement(child).setIsPastNode(isPastNode); // Make an elemenet id match a cue id for style matching purposes. if (!id().isEmpty()) toElement(child).setIdAttribute(id()); } } }
static CSSValueID determineTextDirection(DocumentFragment* vttRoot) { ASSERT(vttRoot); // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the // concatenation of the values of each WebVTT Text Object in nodes, in a // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and // their descendants. TextDirection textDirection = LTR; Node* node = NodeTraversal::next(*vttRoot); while (node) { ASSERT(node->isDescendantOf(vttRoot)); if (node->isTextNode()) { bool hasStrongDirectionality; textDirection = determineDirectionality(node->nodeValue(), hasStrongDirectionality); if (hasStrongDirectionality) break; } else if (node->isVTTElement()) { if (toVTTElement(node)->webVTTNodeType() == VTTNodeTypeRubyText) { node = NodeTraversal::nextSkippingChildren(*node); continue; } } node = NodeTraversal::next(*node); } return isLeftToRightDirection(textDirection) ? CSSValueLtr : CSSValueRtl; }
void VTTCue::markFutureAndPastNodes(ContainerNode* root, double previousTimestamp, double movieTime) { DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp")); bool isPastNode = true; double currentTimestamp = previousTimestamp; if (currentTimestamp > movieTime) isPastNode = false; for (Node* child = root->firstChild(); child; child = NodeTraversal::next(*child, root)) { if (child->nodeName() == timestampTag) { unsigned position = 0; String timestamp = child->nodeValue(); double currentTimestamp; bool check = VTTParser::collectTimeStamp(timestamp, &position, currentTimestamp); ASSERT_UNUSED(check, check); if (currentTimestamp > movieTime) isPastNode = false; } if (child->isVTTElement()) { toVTTElement(child)->setIsPastNode(isPastNode); // Make an elemenet id match a cue id for style matching purposes. if (!id().isEmpty()) toElement(child)->setIdAttribute(id()); } } }
void VTTCue::copyVTTNodeToDOMTree(ContainerNode* vttNode, ContainerNode* parent) { for (Node* node = vttNode->firstChild(); node; node = node->nextSibling()) { RefPtrWillBeRawPtr<Node> clonedNode; if (node->isVTTElement()) clonedNode = toVTTElement(node)->createEquivalentHTMLElement(document()); else clonedNode = node->cloneNode(false); parent->appendChild(clonedNode); if (node->isContainerNode()) copyVTTNodeToDOMTree(toContainerNode(node), toContainerNode(clonedNode)); } }
void VTTCue::updatePastAndFutureNodes(double movieTime) { DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp")); ASSERT(isActive()); // An active cue may still not have a display tree, e.g. if its track is // hidden or if the track belongs to an audio element. if (!m_displayTree) return; // FIXME: Per spec it's possible for neither :past nor :future to match, but // as implemented here and in SelectorChecker they are simply each others // negations. For a cue with no internal timestamps, :past will match but // should not per spec. :future is correct, however. See the spec bug to // determine what the correct behavior should be: // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28237 bool isPastNode = true; double currentTimestamp = startTime(); if (currentTimestamp > movieTime) isPastNode = false; for (Node& child : NodeTraversal::descendantsOf(*m_displayTree)) { if (child.nodeName() == timestampTag) { double currentTimestamp; bool check = VTTParser::collectTimeStamp(child.nodeValue(), currentTimestamp); ASSERT_UNUSED(check, check); if (currentTimestamp > movieTime) isPastNode = false; } if (child.isVTTElement()) { toVTTElement(child).setIsPastNode(isPastNode); // Make an elemenet id match a cue id for style matching purposes. if (!id().isEmpty()) toElement(child).setIdAttribute(id()); } } }
void VTTTreeBuilder::constructTreeFromToken(Document& document) { // http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules switch (m_token.type()) { case VTTTokenTypes::Character: { m_currentNode->parserAppendChild(Text::create(document, m_token.characters())); break; } case VTTTokenTypes::StartTag: { VTTNodeType nodeType = tokenToNodeType(m_token); if (nodeType == VTTNodeTypeNone) break; VTTNodeType currentType = m_currentNode->isVTTElement() ? toVTTElement(m_currentNode.get())->webVTTNodeType() : VTTNodeTypeNone; // <rt> is only allowed if the current node is <ruby>. if (nodeType == VTTNodeTypeRubyText && currentType != VTTNodeTypeRuby) break; RefPtrWillBeRawPtr<VTTElement> child = VTTElement::create(nodeType, &document); if (!m_token.classes().isEmpty()) child->setAttribute(classAttr, m_token.classes()); if (nodeType == VTTNodeTypeVoice) { child->setAttribute(VTTElement::voiceAttributeName(), m_token.annotation()); } else if (nodeType == VTTNodeTypeLanguage) { m_languageStack.append(m_token.annotation()); child->setAttribute(VTTElement::langAttributeName(), m_languageStack.last()); } if (!m_languageStack.isEmpty()) child->setLanguage(m_languageStack.last()); m_currentNode->parserAppendChild(child); m_currentNode = child; break; } case VTTTokenTypes::EndTag: { VTTNodeType nodeType = tokenToNodeType(m_token); if (nodeType == VTTNodeTypeNone) break; // The only non-VTTElement would be the DocumentFragment root. (Text // nodes and PIs will never appear as m_currentNode.) if (!m_currentNode->isVTTElement()) break; VTTNodeType currentType = toVTTElement(m_currentNode.get())->webVTTNodeType(); bool matchesCurrent = nodeType == currentType; if (!matchesCurrent) { // </ruby> auto-closes <rt>. if (currentType == VTTNodeTypeRubyText && nodeType == VTTNodeTypeRuby) { if (m_currentNode->parentNode()) m_currentNode = m_currentNode->parentNode(); } else { break; } } if (nodeType == VTTNodeTypeLanguage) m_languageStack.removeLast(); if (m_currentNode->parentNode()) m_currentNode = m_currentNode->parentNode(); break; } case VTTTokenTypes::TimestampTag: { String charactersString = m_token.characters(); double parsedTimeStamp; if (VTTParser::collectTimeStamp(charactersString, parsedTimeStamp)) m_currentNode->parserAppendChild(ProcessingInstruction::create(document, "timestamp", charactersString)); break; } default: break; } }