TEST_F(ActiveStyleSheetsTest, CompareActiveStyleSheets_AppendedToNonEmpty) { ActiveStyleSheetVector oldSheets; ActiveStyleSheetVector newSheets; HeapVector<Member<RuleSet>> changedRuleSets; CSSStyleSheet* sheet1 = createSheet(); CSSStyleSheet* sheet2 = createSheet(); oldSheets.append(std::make_pair(sheet1, &sheet1->contents()->ruleSet())); newSheets.append(std::make_pair(sheet1, &sheet1->contents()->ruleSet())); newSheets.append(std::make_pair(sheet2, &sheet2->contents()->ruleSet())); EXPECT_EQ(ActiveSheetsAppended, compareActiveStyleSheets(oldSheets, newSheets, changedRuleSets)); EXPECT_EQ(1u, changedRuleSets.size()); }
DispatchEventResult IDBEventDispatcher::dispatch(Event* event, HeapVector<Member<EventTarget>>& eventTargets) { size_t size = eventTargets.size(); ASSERT(size); event->setEventPhase(Event::CAPTURING_PHASE); for (size_t i = size - 1; i; --i) { // Don't do the first element. event->setCurrentTarget(eventTargets[i].get()); eventTargets[i]->fireEventListeners(event); if (event->propagationStopped()) goto doneDispatching; } event->setEventPhase(Event::AT_TARGET); event->setCurrentTarget(eventTargets[0].get()); eventTargets[0]->fireEventListeners(event); if (event->propagationStopped() || !event->bubbles() || event->cancelBubble()) goto doneDispatching; event->setEventPhase(Event::BUBBLING_PHASE); for (size_t i = 1; i < size; ++i) { // Don't do the first element. event->setCurrentTarget(eventTargets[i].get()); eventTargets[i]->fireEventListeners(event); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } doneDispatching: event->setCurrentTarget(nullptr); event->setEventPhase(Event::NONE); return EventTarget::dispatchEventResult(*event); }
static void addRules(RuleSet* ruleSet, const HeapVector<MinimalRuleData>& rules) { for (unsigned i = 0; i < rules.size(); ++i) { const MinimalRuleData& info = rules[i]; ruleSet->addRule(info.m_rule, info.m_selectorIndex, info.m_flags); } }
TEST_F(CustomElementUpgradeSorterTest, oneCandidate) { NonThrowableExceptionState noExceptions; Element* element = document()->createElement("a-a", StringOrDictionary(), noExceptions); document()->documentElement()->appendChild(element); CustomElementUpgradeSorter sorter; sorter.add(element); HeapVector<Member<Element>> elements; sorter.sorted(&elements, document()); EXPECT_EQ(1u, elements.size()) << "exactly one candidate should be in the result set"; EXPECT_TRUE(elements.contains(element)) << "the candidate should be the element that was added"; }
void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadowV0* elementShadow) { DistributedNodes distributedNodes; for (size_t i = 0; i < m_nodes.size(); ++i) { if (m_distributed[i]) continue; if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i)) continue; Node* node = m_nodes[i]; distributedNodes.append(node); elementShadow->didDistributeNode(node, insertionPoint); m_distributed[i] = true; } // Distributes fallback elements if (insertionPoint->isContentInsertionPoint() && distributedNodes.isEmpty()) { for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) { distributedNodes.append(fallbackNode); elementShadow->didDistributeNode(fallbackNode, insertionPoint); } } insertionPoint->setDistributedNodes(distributedNodes); }
void LoadableTextTrack::addRegions(const HeapVector<Member<VTTRegion>>& newRegions) { for (size_t i = 0; i < newRegions.size(); ++i) { newRegions[i]->setTrack(this); regions()->add(newRegions[i]); } }
StyleEngineTest::RuleSetInvalidation StyleEngineTest::scheduleInvalidationsForRules(TreeScope& treeScope, const String& cssText) { StyleSheetContents* sheet = StyleSheetContents::create(CSSParserContext(HTMLStandardMode, nullptr)); sheet->parseString(cssText); HeapVector<Member<RuleSet>> ruleSets; RuleSet& ruleSet = sheet->ensureRuleSet(MediaQueryEvaluator(), RuleHasDocumentSecurityOrigin); ruleSet.compactRulesIfNeeded(); if (ruleSet.needsFullRecalcForRuleSetInvalidation()) return RuleSetInvalidationFullRecalc; ruleSets.append(&ruleSet); styleEngine().scheduleInvalidationsForRuleSets(treeScope, ruleSets); return RuleSetInvalidationsScheduled; }
ScriptPromise NavigatorRequestMediaKeySystemAccess::requestMediaKeySystemAccess( ScriptState* scriptState, Navigator& navigator, const String& keySystem, const HeapVector<MediaKeySystemConfiguration>& supportedConfigurations) { WTF_LOG(Media, "NavigatorRequestMediaKeySystemAccess::requestMediaKeySystemAccess()"); // From https://w3c.github.io/encrypted-media/#requestMediaKeySystemAccess // When this method is invoked, the user agent must run the following steps: // 1. If keySystem is an empty string, return a promise rejected with a // new DOMException whose name is InvalidAccessError. if (keySystem.isEmpty()) { return ScriptPromise::rejectWithDOMException( scriptState, DOMException::create(InvalidAccessError, "The keySystem parameter is empty.")); } // 2. If supportedConfigurations was provided and is empty, return a // promise rejected with a new DOMException whose name is // InvalidAccessError. if (!supportedConfigurations.size()) { return ScriptPromise::rejectWithDOMException( scriptState, DOMException::create(InvalidAccessError, "The supportedConfigurations parameter is empty.")); } // 3-4. 'May Document use powerful features?' check. ExecutionContext* executionContext = scriptState->executionContext(); String errorMessage; if (executionContext->isPrivilegedContext(errorMessage)) { UseCounter::count(executionContext, UseCounter::EncryptedMediaSecureOrigin); } else { UseCounter::countDeprecation(executionContext, UseCounter::EncryptedMediaInsecureOrigin); // TODO(ddorwin): Implement the following: // Reject promise with a new DOMException whose name is NotSupportedError. } // 5. Let origin be the origin of document. // (Passed with the execution context in step 7.) // 6. Let promise be a new promise. Document* document = toDocument(executionContext); if (!document->page()) { return ScriptPromise::rejectWithDOMException( scriptState, DOMException::create(InvalidStateError, "The context provided is not associated with a page.")); } MediaKeySystemAccessInitializer* initializer = new MediaKeySystemAccessInitializer(scriptState, keySystem, supportedConfigurations); ScriptPromise promise = initializer->promise(); // 7. Asynchronously determine support, and if allowed, create and // initialize the MediaKeySystemAccess object. MediaKeysController* controller = MediaKeysController::from(document->page()); WebEncryptedMediaClient* mediaClient = controller->encryptedMediaClient(executionContext); mediaClient->requestMediaKeySystemAccess(WebEncryptedMediaRequest(initializer)); // 8. Return promise. return promise; }
inline void DistributionPool::detachNonDistributedNodes() { for (size_t i = 0; i < m_nodes.size(); ++i) { if (m_distributed[i]) continue; if (m_nodes[i]->layoutObject()) m_nodes[i]->lazyReattachIfAttached(); } }
MIDIInputMap* MIDIAccess::inputs() const { HeapVector<Member<MIDIInput>> inputs; HashSet<String> ids; for (size_t i = 0; i < m_inputs.size(); ++i) { MIDIInput* input = m_inputs[i]; if (input->getState() != PortState::MIDIPortStateDisconnected) { inputs.append(input); ids.add(input->id()); } } if (inputs.size() != ids.size()) { // There is id duplication that violates the spec. inputs.clear(); } return new MIDIInputMap(inputs); }
TEST_F(ActiveStyleSheetsTest, CompareActiveStyleSheets_RemoveNullRuleSet) { ActiveStyleSheetVector oldSheets; ActiveStyleSheetVector newSheets; HeapVector<Member<RuleSet>> changedRuleSets; CSSStyleSheet* sheet1 = createSheet(); CSSStyleSheet* sheet2 = createSheet(); oldSheets.append(std::make_pair(sheet1, &sheet1->contents()->ruleSet())); oldSheets.append(std::make_pair(sheet2, nullptr)); newSheets.append(std::make_pair(sheet1, &sheet1->contents()->ruleSet())); EXPECT_EQ(NoActiveSheetsChanged, compareActiveStyleSheets(oldSheets, newSheets, changedRuleSets)); EXPECT_EQ(0u, changedRuleSets.size()); }
TEST_F(CustomElementUpgradeSorterTest, inOtherDocument_notInSet) { NonThrowableExceptionState noExceptions; Element* element = document()->createElement("a-a", StringOrDictionary(), noExceptions); Document* otherDocument = HTMLDocument::create(); otherDocument->appendChild(element); EXPECT_EQ(otherDocument, element->ownerDocument()) << "sanity: another document should have adopted an element on append"; CustomElementUpgradeSorter sorter; sorter.add(element); HeapVector<Member<Element>> elements; sorter.sorted(&elements, document()); EXPECT_EQ(0u, elements.size()) << "the adopted-away candidate should not have been included"; }
void ScopedStyleResolver::addFontFaceRules(const RuleSet& ruleSet) { // FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for // the moment. if (!treeScope().rootNode().isDocumentNode()) return; Document& document = treeScope().document(); CSSFontSelector* cssFontSelector = document.styleEngine().fontSelector(); const HeapVector<Member<StyleRuleFontFace>> fontFaceRules = ruleSet.fontFaceRules(); for (auto& fontFaceRule : fontFaceRules) { if (FontFace* fontFace = FontFace::create(&document, fontFaceRule)) cssFontSelector->fontFaceCache()->add(cssFontSelector, fontFaceRule, fontFace); } if (fontFaceRules.size()) document.styleResolver()->invalidateMatchedPropertiesCache(); }
void LoadFontPromiseResolver::loadFonts(ExecutionContext* context) { if (!m_numLoading) { m_resolver->resolve(m_fontFaces); return; } for (size_t i = 0; i < m_fontFaces.size(); i++) m_fontFaces[i]->loadWithCallback(this, context); }
inline void DistributionPool::populateChildren(const ContainerNode& parent) { clear(); for (Node* child = parent.firstChild(); child; child = child->nextSibling()) { if (isHTMLSlotElement(child)) { // TODO(hayato): Support re-distribution across v0 and v1 shadow trees continue; } if (isActiveInsertionPoint(*child)) { InsertionPoint* insertionPoint = toInsertionPoint(child); for (size_t i = 0; i < insertionPoint->distributedNodesSize(); ++i) m_nodes.append(insertionPoint->distributedNodeAt(i)); } else { m_nodes.append(child); } } m_distributed.resize(m_nodes.size()); m_distributed.fill(false); }
void ElementShadowV0::distribute() { HeapVector<Member<HTMLShadowElement>, 32> shadowInsertionPoints; DistributionPool pool(m_elementShadow->host()); for (ShadowRoot* root = &youngestShadowRoot(); root; root = root->olderShadowRoot()) { HTMLShadowElement* shadowInsertionPoint = 0; for (const auto& point : root->descendantInsertionPoints()) { if (!point->isActive()) continue; if (isHTMLShadowElement(*point)) { DCHECK(!shadowInsertionPoint); shadowInsertionPoint = toHTMLShadowElement(point); shadowInsertionPoints.append(shadowInsertionPoint); } else { pool.distributeTo(point, this); if (ElementShadow* shadow = shadowWhereNodeCanBeDistributedForV0(*point)) shadow->setNeedsDistributionRecalc(); } } } for (size_t i = shadowInsertionPoints.size(); i > 0; --i) { HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1]; ShadowRoot* root = shadowInsertionPoint->containingShadowRoot(); DCHECK(root); if (root->isOldest()) { pool.distributeTo(shadowInsertionPoint, this); } else if (root->olderShadowRoot()->type() == root->type()) { // Only allow reprojecting older shadow roots between the same type to // disallow reprojecting UA elements into author shadows. DistributionPool olderShadowRootPool(*root->olderShadowRoot()); olderShadowRootPool.distributeTo(shadowInsertionPoint, this); root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot( shadowInsertionPoint); } if (ElementShadow* shadow = shadowWhereNodeCanBeDistributedForV0(*shadowInsertionPoint)) shadow->setNeedsDistributionRecalc(); } InspectorInstrumentation::didPerformElementShadowDistribution( &m_elementShadow->host()); }
HeapVector<FormDataEntryValue> FormData::getAll(const String& name) { HeapVector<FormDataEntryValue> results; const CString encodedName = encodeAndNormalize(name); for (const auto& entry : entries()) { if (entry->name() != encodedName) continue; FormDataEntryValue value; if (entry->isString()) { value.setUSVString(decode(entry->value())); } else { ASSERT(entry->isFile()); value.setFile(entry->file()); } results.append(value); } return results; }
FontFaceSetIterable::IterationSource* FontFaceSet::startIteration( ScriptState*, ExceptionState&) { // Setlike should iterate each item in insertion order, and items should // be keep on up to date. But since blink does not have a way to hook up CSS // modification, take a snapshot here, and make it ordered as follows. HeapVector<Member<FontFace>> fontFaces; if (inActiveDocumentContext()) { const HeapListHashSet<Member<FontFace>>& cssConnectedFaces = cssConnectedFontFaceList(); fontFaces.reserveInitialCapacity(cssConnectedFaces.size() + m_nonCSSConnectedFaces.size()); for (const auto& fontFace : cssConnectedFaces) fontFaces.append(fontFace); for (const auto& fontFace : m_nonCSSConnectedFaces) fontFaces.append(fontFace); } return new IterationSource(fontFaces); }
static void completeURLs(DocumentFragment& fragment, const String& baseURL) { HeapVector<AttributeChange> changes; KURL parsedBaseURL(ParsedURLString, baseURL); for (Element& element : ElementTraversal::descendantsOf(fragment)) { AttributeCollection attributes = element.attributes(); // AttributeCollection::iterator end = attributes.end(); for (const auto& attribute : attributes) { if (element.isURLAttribute(attribute) && !attribute.value().isEmpty()) changes.append(AttributeChange( &element, attribute.name(), KURL(parsedBaseURL, attribute.value()).getString())); } } for (auto& change : changes) change.apply(); }
void HTMLFormControlsCollection::namedGetter( const AtomicString& name, RadioNodeListOrElement& returnValue) { HeapVector<Member<Element>> namedItems; this->namedItems(name, namedItems); if (namedItems.isEmpty()) return; if (namedItems.size() == 1) { if (!isHTMLImageElement(*namedItems[0])) returnValue.setElement(namedItems.at(0)); return; } // This path never returns a RadioNodeList for <img> because // onlyMatchingImgElements flag is false by default. returnValue.setRadioNodeList(ownerNode().radioNodeList(name)); }
TEST_F(CustomElementRegistryTest, collectCandidates_shouldNotIncludeElementsInDifferentDocument) { Element* element = CreateElement("a-a").inDocument(&document()); registry().addCandidate(element); Document* otherDocument = HTMLDocument::create(); otherDocument->appendChild(element); EXPECT_EQ(otherDocument, element->ownerDocument()) << "sanity: another document should have adopted an element on append"; HeapVector<Member<Element>> elements; collectCandidates(CustomElementDescriptor("a-a", "a-a"), &elements); EXPECT_TRUE(elements.isEmpty()) << "no candidates should have been found, but we have " << elements.size(); EXPECT_FALSE(elements.contains(element)) << "the adopted-away candidate should not have been found"; }
PaymentAppRequestData PaymentAppRequestDataConversion::toPaymentAppRequestData( ScriptState* scriptState, const WebPaymentAppRequestData& webData) { PaymentAppRequestData data; data.setOrigin(webData.origin); HeapVector<PaymentMethodData> methodData; for (const auto& md : webData.methodData) { methodData.append(toPaymentMethodData(scriptState, md)); } data.setMethodData(methodData); data.setTotal(toPaymentItem(webData.total)); HeapVector<PaymentDetailsModifier> modifiers; for (const auto& modifier : webData.modifiers) { modifiers.append(toPaymentDetailsModifier(scriptState, modifier)); } data.setOptionId(webData.optionId); return data; }
bool TouchEventManager::generateTouchInfosAfterHittest( const PlatformTouchEvent& event, HeapVector<TouchInfo>& touchInfos) { bool newTouchSequence = true; bool allTouchesReleased = true; for (const auto& point : event.touchPoints()) { if (point.state() != PlatformTouchPoint::TouchPressed) newTouchSequence = false; if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled) allTouchesReleased = false; } if (newTouchSequence) { // Ideally we'd ASSERT(!m_touchSequenceDocument) here since we should // have cleared the active document when we saw the last release. But we // have some tests that violate this, ClusterFuzz could trigger it, and // there may be cases where the browser doesn't reliably release all // touches. http://crbug.com/345372 tracks this. m_touchSequenceDocument.clear(); m_touchSequenceUserGestureToken.clear(); } ASSERT(m_frame->view()); if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || !m_touchSequenceDocument->frame()->view())) { // If the active touch document has no frame or view, it's probably being destroyed // so we can't dispatch events. return false; } for (const auto& point : event.touchPoints()) { TouchEventManager::TouchInfo touchInfo; touchInfo.point = point; touchInfos.append(touchInfo); } updateTargetAndRegionMapsForTouchStarts(touchInfos); m_touchPressed = !allTouchesReleased; // If there's no document receiving touch events, or no handlers on the // document set to receive the events, then we can skip all the rest of // this work. if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || !hasTouchHandlers(m_touchSequenceDocument->frameHost()->eventHandlerRegistry()) || !m_touchSequenceDocument->frame()) { if (allTouchesReleased) { m_touchSequenceDocument.clear(); m_touchSequenceUserGestureToken.clear(); } return false; } setAllPropertiesOfTouchInfos(touchInfos); return true; }
void TextFinder::updateFindMatchRects() { IntSize currentContentsSize = ownerFrame().contentsSize(); if (m_contentsSizeForCurrentFindMatchRects != currentContentsSize) { m_contentsSizeForCurrentFindMatchRects = currentContentsSize; m_findMatchRectsAreValid = false; } size_t deadMatches = 0; for (FindMatch& match : m_findMatchesCache) { if (!match.m_range->boundaryPointsValid() || !match.m_range->startContainer()->isConnected()) match.m_rect = FloatRect(); else if (!m_findMatchRectsAreValid) match.m_rect = findInPageRectFromRange(match.m_range.get()); if (match.m_rect.isEmpty()) ++deadMatches; } // Remove any invalid matches from the cache. if (deadMatches) { HeapVector<FindMatch> filteredMatches; filteredMatches.reserveCapacity(m_findMatchesCache.size() - deadMatches); for (const FindMatch& match : m_findMatchesCache) { if (!match.m_rect.isEmpty()) filteredMatches.append(match); } m_findMatchesCache.swap(filteredMatches); } // Invalidate the rects in child frames. Will be updated later during // traversal. if (!m_findMatchRectsAreValid) for (WebFrame* child = ownerFrame().firstChild(); child; child = child->nextSibling()) toWebLocalFrameImpl(child)->ensureTextFinder().m_findMatchRectsAreValid = false; m_findMatchRectsAreValid = true; }
void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate& update, const Element* animatingElement, double timelineCurrentTime) { ElementAnimations* elementAnimations = animatingElement ? animatingElement->elementAnimations() : nullptr; AnimationStack* animationStack = elementAnimations ? &elementAnimations->defaultStack() : nullptr; if (update.newAnimations().isEmpty() && update.suppressedAnimations().isEmpty()) { ActiveInterpolationsMap activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, 0, 0, KeyframeEffect::DefaultPriority, timelineCurrentTime)); update.adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations); return; } HeapVector<Member<InertEffect>> newEffects; for (const auto& newAnimation : update.newAnimations()) newEffects.append(newAnimation.effect.get()); for (const auto& updatedAnimation : update.animationsWithUpdates()) newEffects.append(updatedAnimation.effect.get()); // Animations with updates use a temporary InertEffect for the current frame. ActiveInterpolationsMap activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, &newEffects, &update.suppressedAnimations(), KeyframeEffect::DefaultPriority, timelineCurrentTime)); update.adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations); }
Nullable<HeapVector<WebCLImageDescriptor>> WebCLContext::getSupportedImageFormats(unsigned memFlags, ExceptionState& es) { HeapVector<WebCLImageDescriptor> supportedImageDescriptor; if (isReleased()) { es.throwWebCLException(WebCLException::InvalidContext, WebCLException::invalidContextMessage); return supportedImageDescriptor; } if (!WebCLInputChecker::isValidMemoryObjectFlag(memFlags)) { es.throwWebCLException(WebCLException::InvalidValue, WebCLException::invalidValueMessage); return supportedImageDescriptor; } cl_uint numberOfSupportedImageFormats = 0; cl_int err = clGetSupportedImageFormats(m_clContext, memFlags, CL_MEM_OBJECT_IMAGE2D, 0, 0, &numberOfSupportedImageFormats); if (err != CL_SUCCESS) { es.throwWebCLException(WebCLException::InvalidImageSize, WebCLException::invalidImageSizeMessage); return supportedImageDescriptor; } Vector<cl_image_format> supportedImages; supportedImages.reserveCapacity(numberOfSupportedImageFormats); supportedImages.resize(numberOfSupportedImageFormats); err = clGetSupportedImageFormats(m_clContext, memFlags, CL_MEM_OBJECT_IMAGE2D, numberOfSupportedImageFormats, supportedImages.data(), 0); if (err != CL_SUCCESS) { WebCLException::throwException(err, es); } else { for (size_t i = 0; i < static_cast<unsigned>(numberOfSupportedImageFormats); ++i) { if (WebCLInputChecker::isValidChannelOrder(supportedImages[i].image_channel_order) && WebCLInputChecker::isValidChannelType(supportedImages[i].image_channel_data_type)) { WebCLImageDescriptor des; des.setChannelOrder(supportedImages[i].image_channel_order); des.setChannelType(supportedImages[i].image_channel_data_type); supportedImageDescriptor.append(des); } } } return supportedImageDescriptor; }
void NodeSet::sort() const { if (m_isSorted) return; unsigned nodeCount = m_nodes.size(); if (nodeCount < 2) { const_cast<bool&>(m_isSorted) = true; return; } if (nodeCount > traversalSortCutoff) { traversalSort(); return; } bool containsAttributeNodes = false; HeapVector<NodeSetVector> parentMatrix(nodeCount); for (unsigned i = 0; i < nodeCount; ++i) { NodeSetVector& parentsVector = parentMatrix[i]; Node* n = m_nodes[i].get(); parentsVector.append(n); if (n->isAttributeNode()) { n = toAttr(n)->ownerElement(); parentsVector.append(n); containsAttributeNodes = true; } for (n = n->parentNode(); n; n = n->parentNode()) parentsVector.append(n); } sortBlock(0, nodeCount, parentMatrix, containsAttributeNodes); // It is not possible to just assign the result to m_nodes, because some // nodes may get dereferenced and destroyed. HeapVector<Member<Node>> sortedNodes; sortedNodes.reserveInitialCapacity(nodeCount); for (unsigned i = 0; i < nodeCount; ++i) sortedNodes.append(parentMatrix[i][0]); const_cast<HeapVector<Member<Node>>&>(m_nodes).swap(sortedNodes); }
Element* SlotScopedTraversal::previous(const Element& current) { Element* nearestAncestorAssignedToSlot = SlotScopedTraversal::nearestAncestorAssignedToSlot(current); ASSERT(nearestAncestorAssignedToSlot); // NodeTraversal within nearestAncestorAssignedToSlot if (Element* previous = ElementTraversal::previous(current, nearestAncestorAssignedToSlot)) return previous; // If null, jump to previous assigned node's descendant const HeapVector<Member<Node>> assignedNodes = nearestAncestorAssignedToSlot->assignedSlot()->getAssignedNodes(); size_t currentIndex = assignedNodes.reverseFind(*nearestAncestorAssignedToSlot); ASSERT(currentIndex != kNotFound); for (; currentIndex > 0; --currentIndex) { const Member<Node> assignedPrevious = assignedNodes[currentIndex - 1]; if (assignedPrevious->isElementNode()) { if (Element* last = ElementTraversal::lastWithin(*toElement(assignedPrevious))) return last; return toElement(assignedPrevious); } } return nullptr; }
TEST_F(CustomElementUpgradeSorterTest, candidatesInDocumentOrder) { Element* a = createElementWithId("a-a", "a"); Element* b = createElementWithId("a-a", "b"); Element* c = createElementWithId("a-a", "c"); document()->documentElement()->appendChild(a); a->appendChild(b); document()->documentElement()->appendChild(c); CustomElementUpgradeSorter sorter; sorter.add(b); sorter.add(a); sorter.add(c); HeapVector<Member<Element>> elements; sorter.sorted(&elements, document()); EXPECT_EQ(3u, elements.size()); EXPECT_EQ(a, elements[0].get()); EXPECT_EQ(b, elements[1].get()); EXPECT_EQ(c, elements[2].get()); }
TEST_F(ActiveStyleSheetsTest, CompareActiveStyleSheets_InsertedAndRemoved) { ActiveStyleSheetVector oldSheets; ActiveStyleSheetVector newSheets; HeapVector<Member<RuleSet>> changedRuleSets; CSSStyleSheet* sheet1 = createSheet(); CSSStyleSheet* sheet2 = createSheet(); CSSStyleSheet* sheet3 = createSheet(); oldSheets.append(std::make_pair(sheet1, &sheet1->contents()->ruleSet())); oldSheets.append(std::make_pair(sheet2, &sheet2->contents()->ruleSet())); newSheets.append(std::make_pair(sheet2, &sheet2->contents()->ruleSet())); newSheets.append(std::make_pair(sheet3, &sheet3->contents()->ruleSet())); EXPECT_EQ(ActiveSheetsChanged, compareActiveStyleSheets(oldSheets, newSheets, changedRuleSets)); ASSERT_EQ(2u, changedRuleSets.size()); EXPECT_EQ(&sheet1->contents()->ruleSet(), changedRuleSets[0]); EXPECT_EQ(&sheet3->contents()->ruleSet(), changedRuleSets[1]); }