TEST_F(CustomElementRegistryTest, collectCandidates_shouldOnlyIncludeCandidatesMatchingDescriptor) { CustomElementDescriptor descriptor("hello-world", "hello-world"); // Does not match: namespace is not HTML Element* elementA = CreateElement("hello-world") .inDocument(&document()) .inNamespace("data:text/date,1981-03-10"); // Matches Element* elementB = CreateElement("hello-world").inDocument(&document()); // Does not match: local name is not hello-world Element* elementC = CreateElement("button") .inDocument(&document()) .withIsAttribute("hello-world"); document().documentElement()->appendChild(elementA); elementA->appendChild(elementB); elementA->appendChild(elementC); registry().addCandidate(elementA); registry().addCandidate(elementB); registry().addCandidate(elementC); HeapVector<Member<Element>> elements; collectCandidates(descriptor, &elements); EXPECT_EQ(1u, elements.size()) << "only one candidates should have been found"; EXPECT_EQ(elementB, elements[0]) << "the matching element should have been found"; }
TEST_F(CustomElementRegistryTest, collectCandidates_oneCandidate) { Element* element = CreateElement("a-a").inDocument(&document()); registry().addCandidate(element); document().documentElement()->appendChild(element); HeapVector<Member<Element>> elements; collectCandidates(CustomElementDescriptor("a-a", "a-a"), &elements); EXPECT_EQ(1u, elements.size()) << "exactly one candidate should have been found"; EXPECT_TRUE(elements.contains(element)) << "the candidate should be the element that was added"; }
TEST_F(CustomElementRegistryTest, collectCandidates_shouldNotIncludeElementsRemovedFromDocument) { Element* element = CreateElement("a-a").inDocument(&document()); registry().addCandidate(element); 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 out-of-document candidate should not have been found"; }
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"; }
TEST_F(CustomElementRegistryTest, collectCandidates_shouldBeInDocumentOrder) { CreateElement factory = CreateElement("a-a"); factory.inDocument(&document()); Element* elementA = factory.withId("a"); Element* elementB = factory.withId("b"); Element* elementC = factory.withId("c"); registry().addCandidate(elementB); registry().addCandidate(elementA); registry().addCandidate(elementC); document().documentElement()->appendChild(elementA); elementA->appendChild(elementB); document().documentElement()->appendChild(elementC); HeapVector<Member<Element>> elements; collectCandidates(CustomElementDescriptor("a-a", "a-a"), &elements); EXPECT_EQ(elementA, elements[0].get()); EXPECT_EQ(elementB, elements[1].get()); EXPECT_EQ(elementC, elements[2].get()); }
// http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition void CustomElementRegistry::define(const AtomicString& name, CustomElementDefinitionBuilder& builder, const ElementDefinitionOptions& options, ExceptionState& exceptionState) { if (!builder.checkConstructorIntrinsics()) return; if (throwIfInvalidName(name, exceptionState)) return; if (nameIsDefined(name) || v0NameIsDefined(name)) { exceptionState.throwDOMException( NotSupportedError, "this name has already been used with this registry"); return; } if (!builder.checkConstructorNotRegistered()) return; // Step 7. customized built-in elements definition // element interface extends option checks if (RuntimeEnabledFeatures::customElementsBuiltinEnabled() && options.hasExtends()) { // If element interface is valid custom element name, throw exception if (throwIfValidName(AtomicString(options.extends()), exceptionState)) return; // If element interface is undefined element, throw exception // Set localname to extends } // TODO(dominicc): Add a test where the prototype getter destroys // the context. // 8. If this CustomElementRegistry's element definition is // running flag is set, then throw a "NotSupportedError" // DOMException and abort these steps. if (m_elementDefinitionIsRunning) { exceptionState.throwDOMException( NotSupportedError, "an element definition is already being processed"); return; } { // 9. Set this CustomElementRegistry's element definition is // running flag. ElementDefinitionIsRunning defining(m_elementDefinitionIsRunning); // 10.1-2 if (!builder.checkPrototype()) return; // 10.3-6 if (!builder.rememberOriginalProperties()) return; // "Then, perform the following substep, regardless of whether // the above steps threw an exception or not: Unset this // CustomElementRegistry's element definition is running // flag." // (ElementDefinitionIsRunning destructor does this.) } CustomElementDescriptor descriptor(name, name); CustomElementDefinition* definition = builder.build(descriptor); CHECK(!exceptionState.hadException()); CHECK(definition->descriptor() == descriptor); DefinitionMap::AddResult result = m_definitions.add(descriptor.name(), definition); CHECK(result.isNewEntry); HeapVector<Member<Element>> candidates; collectCandidates(descriptor, &candidates); for (Element* candidate : candidates) definition->enqueueUpgradeReaction(candidate); // 16: when-defined promise processing const auto& entry = m_whenDefinedPromiseMap.find(name); if (entry == m_whenDefinedPromiseMap.end()) return; entry->value->resolve(); m_whenDefinedPromiseMap.remove(entry); }
std::map<int, std::vector<CaptionStart>> extractCaptionsFromText(const std::vector<TextPage *> &textPages, bool verbose) { CandidateCollection candidates = collectCandidates(textPages); // In order to be considered ColonOnly f1 = ColonOnly(); PeriodOnly f2 = PeriodOnly(); BoldOnly f3 = BoldOnly(); ItalicOnly f4 = ItalicOnly(); AllCapsFiguresOnly f5 = AllCapsFiguresOnly(); AbbrevFiguresOnly f6 = AbbrevFiguresOnly(); NoNextWord f7 = NoNextWord(); BlockStartOnly f8 = BlockStartOnly(); LineStartOnly f9 = LineStartOnly(); NextWordOnly f10 = NextWordOnly(); std::vector<CandidateFilter *> filters = {&f1, &f2, &f3, &f4, &f5, &f6, &f7, &f8, &f9, &f10}; if (verbose) { printf("Scanning for captions...\n"); int nCandidates = 0; for (auto &ccs : candidates) { nCandidates += ccs.second->size(); } printf("Collected %d candidates for %d detected captions\n", nCandidates, (int)candidates.size()); } bool triedAll = false; while (anyDuplicates(candidates) and not triedAll) { triedAll = true; for (CandidateFilter *cf : filters) { if (applyFilter(cf, candidates)) { if (verbose) { int nCandidates = 0; for (auto &ccs : candidates) { nCandidates += ccs.second->size(); } printf("Applied filter %s (%d remain)\n", cf->name, nCandidates); } triedAll = false; break; } } } // Check for non consecutive figures / tables, add any found as errors if (verbose) { int maxTable = 0; int maxFigure = 0; int nTables = 0; int nFigures = 0; for (auto &ccs : candidates) { if (ccs.first > 0) { nFigures++; maxFigure = std::max(ccs.first, maxFigure); } else { nTables++; maxTable = std::max(-ccs.first, maxTable); } } if (maxTable != nTables) { printf("Warning: Max table number found was %d, but only found %d table " "captions!\n", maxTable, nTables); } if (maxFigure != nFigures) { printf( "Warning: Max figure number found was %d, but only found %d figure " "captions!\n", maxFigure, nFigures); } } std::map<int, std::vector<CaptionStart>> output = std::map<int, std::vector<CaptionStart>>(); // Add in all the captions for (CandidateCollection::iterator it = candidates.begin(); it != candidates.end();) { std::vector<CaptionCandidate> *captionOptions = it->second.get(); if (captionOptions->size() <= 2) { if (verbose && captionOptions->size() == 2) { // This might be due to a continued Figure, but even if it is a mistake // we can hope the following steps will not find any figure regions // for the incorrect candidate we selected printf("Two candidates for %s%d, keeping both\n", getFigureTypeString(captionOptions->at(0).type), captionOptions->at(0).number); } for (CaptionCandidate cc : *captionOptions) { output[cc.page].push_back( CaptionStart(cc.page, cc.number, cc.word, cc.type)); } } else if (verbose && captionOptions->size() > 0) { printf("%d candidates for %s%d, excluding them\n", (int)captionOptions->size(), getFigureTypeString(captionOptions->at(0).type), captionOptions->at(0).number); } candidates.erase(it++); } if (verbose) printf("Done parsing captions.\n\n"); return output; }