void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) { EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); if (iter == m_eventListeners.end()) return; ListenerVector& listeners = iter->second; for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) if (*listenerIter == eventListener) { listeners.remove(listenerIter - listeners.begin()); return; } }
static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) { CounterNode* previous; for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) { previous = child->previousInPreOrder(); child->parent()->removeChild(child.get(), identifier); ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); counterMaps().get(child->renderer())->remove(identifier.impl()); if (!child->renderer()->documentBeingDestroyed()) { RenderObjectChildList* children = child->renderer()->virtualChildren(); if (children) children->invalidateCounters(child->renderer(), identifier); } } RenderObject* renderer = node->renderer(); if (!renderer->documentBeingDestroyed()) { if (RenderObjectChildList* children = renderer->virtualChildren()) children->invalidateCounters(renderer, identifier); } if (CounterNode* parent = node->parent()) parent->removeChild(node, identifier); }
static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) { if (ns.isEmpty()) return; // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); AtomicStringImpl* foundNS = namespaces.get(pre); if (foundNS != ns.impl()) { namespaces.set(pre, ns.impl()); DEFINE_STATIC_LOCAL(const String, xmlns, ("xmlns")); result.append(' '); append(result, xmlns); if (!prefix.isEmpty()) { result.append(':'); append(result, prefix); } result.append('='); result.append('"'); appendAttributeValue(result, ns, false); result.append('"'); }
void DOMApplicationCache::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture) { EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); if (iter == m_eventListeners.end()) return; ListenerVector& listeners = iter->second; for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { if (*listenerIter == eventListener) { listeners.remove(listenerIter - listeners.begin()); return; } } }
FormControlState SavedFormState::takeControlState(const AtomicString& name, const AtomicString& type) { if (m_stateForNewFormElements.isEmpty()) return FormControlState(); FormElementStateMap::iterator it = m_stateForNewFormElements.find(FormElementKey(name.impl(), type.impl())); if (it == m_stateForNewFormElements.end()) return FormControlState(); ASSERT(it->value.size()); FormControlState state = it->value.takeFirst(); m_controlStateCount--; if (!it->value.size()) m_stateForNewFormElements.remove(it); return state; }
void Loader::nonCacheRequestComplete(const KURL& url) { if (!url.protocolInHTTPFamily()) return; AtomicString hostName = url.host(); m_hosts.checkConsistency(); RefPtr<Host> host = m_hosts.get(hostName.impl()); ASSERT(host); if (!host) return; host->nonCacheRequestComplete(); }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces, bool allowEmptyDefaultNS) { namespaces.checkConsistency(); if (namespaceURI.isEmpty()) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-serialization-algorithm if (allowEmptyDefaultNS && namespaces.get(emptyAtom.impl())) { result.append(' '); result.append(xmlnsAtom.string()); result.appendLiteral("=\"\""); } return; } // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); AtomicStringImpl* foundNS = namespaces.get(pre); if (foundNS != namespaceURI.impl()) { namespaces.set(pre, namespaceURI.impl()); // Add namespace to prefix pair so we can do constraint checking later. if (inXMLFragmentSerialization() && !prefix.isEmpty()) namespaces.set(namespaceURI.impl(), pre); // Make sure xml prefix and namespace are always known to uphold the constraints listed at http://www.w3.org/TR/xml-names11/#xmlReserved. if (namespaceURI.impl() == XMLNames::xmlNamespaceURI.impl()) return; result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.append('='); result.append('"'); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
PassRefPtr<HTMLElement> HTMLElementFactory::createHTMLElement(const AtomicString& tagName, Document* doc, HTMLFormElement* form, bool createdByParser) { if (!doc) return 0; // Don't allow elements to ever be made without having a doc. if (!gFunctionMap) createFunctionMap(); ConstructorFunc func = gFunctionMap->get(tagName.impl()); if (func) return func(tagName, doc, form, createdByParser); // elements with no special representation in the DOM return new HTMLElement(QualifiedName(nullAtom, tagName, xhtmlNamespaceURI), doc); }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) { namespaces.checkConsistency(); if (namespaceURI.isEmpty()) return; // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); AtomicStringImpl* foundNS = namespaces.get(pre); if (foundNS != namespaceURI.impl()) { namespaces.set(pre, namespaceURI.impl()); result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.append('='); result.append('"'); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
HTMLMapElement* TreeScope::getImageMap(const String& url) const { if (url.isNull()) return nullptr; if (!m_imageMapsByName) return nullptr; size_t hashPos = url.find('#'); String name = (hashPos == notFound ? String() : url.substring(hashPos + 1)).impl(); if (name.isEmpty()) return nullptr; if (m_rootNode.document().isHTMLDocument()) { AtomicString lowercasedName = name.lower(); return m_imageMapsByName->getElementByLowercasedMapName(*lowercasedName.impl(), *this); } return m_imageMapsByName->getElementByMapName(*AtomicString(name).impl(), *this); }
bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t) { m_keyframeAnimations.checkConsistency(); RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name.impl()); if (!keyframeAnim || !keyframeAnim->running()) return false; double count = keyframeAnim->m_animation->iterationCount(); if ((t >= 0.0) && ((count == Animation::IterationCountInfinite) || (t <= count * keyframeAnim->duration()))) { keyframeAnim->freezeAtTime(t); return true; } return false; }
bool CompositeAnimationPrivate::pauseAnimationAtTime(const AtomicString& name, double t) { if (!name) return false; RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name.impl()); if (!keyframeAnim || !keyframeAnim->running()) return false; int count = keyframeAnim->m_animation->iterationCount(); if ((t >= 0.0) && (!count || (t <= count * keyframeAnim->duration()))) { keyframeAnim->pauseAtTime(t); return true; } return false; }
static void removeItemFromMap(HTMLDocument::NameCountMap& map, const AtomicString& name) { if (name.isEmpty()) return; HTMLDocument::NameCountMap::iterator it = map.find(name.impl()); if (it == map.end()) return; int oldVal = it->second; ASSERT(oldVal != 0); int newVal = oldVal - 1; if (newVal == 0) map.remove(it); else it->second = newVal; }
void Loader::servePendingRequests(Priority minimumPriority) { m_requestTimer.stop(); m_nonHTTPProtocolHost.servePendingRequests(minimumPriority); Vector<Host*> hostsToServe; copyValuesToVector(m_hosts, hostsToServe); for (unsigned n = 0; n < hostsToServe.size(); ++n) { Host* host = hostsToServe[n]; if (host->hasRequests()) host->servePendingRequests(minimumPriority); else if (!host->processingResource()){ AtomicString name = host->name(); delete host; m_hosts.remove(name.impl()); } } }
void HTMLHRElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style) { if (name == alignAttr) { if (equalLettersIgnoringASCIICase(value, "left")) { addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginLeft, 0, CSSPrimitiveValue::CSS_PX); addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginRight, CSSValueAuto); } else if (equalLettersIgnoringASCIICase(value, "right")) { addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginLeft, CSSValueAuto); addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginRight, 0, CSSPrimitiveValue::CSS_PX); } else { addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginLeft, CSSValueAuto); addPropertyToPresentationAttributeStyle(style, CSSPropertyMarginRight, CSSValueAuto); } } else if (name == widthAttr) { bool ok; int v = value.toInt(&ok); if (ok && !v) addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, 1, CSSPrimitiveValue::CSS_PX); else addHTMLLengthToStyle(style, CSSPropertyWidth, value); } else if (name == colorAttr) { addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderStyle, CSSValueSolid); addHTMLColorToStyle(style, CSSPropertyBorderColor, value); addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value); } else if (name == noshadeAttr) { if (!hasAttributeWithoutSynchronization(colorAttr)) { addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderStyle, CSSValueSolid); RefPtr<CSSPrimitiveValue> darkGrayValue = CSSValuePool::singleton().createColorValue(Color::darkGray); style.setProperty(CSSPropertyBorderColor, darkGrayValue); style.setProperty(CSSPropertyBackgroundColor, darkGrayValue); } } else if (name == sizeAttr) { StringImpl* si = value.impl(); int size = si->toInt(); if (size <= 1) addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderBottomWidth, 0, CSSPrimitiveValue::CSS_PX); else addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, size - 2, CSSPrimitiveValue::CSS_PX); } else HTMLElement::collectStyleForPresentationAttribute(name, value, style); }
void FormController::formStatesFromStateVector(const Vector<String>& stateVector, SavedFormStateMap& map) { map.clear(); size_t i = 0; if (stateVector.size() < 1 || stateVector[i++] != formStateSignature()) return; while (i + 1 < stateVector.size()) { AtomicString formKey = stateVector[i++]; auto state = SavedFormState::deserialize(stateVector, i); if (!state) { i = 0; break; } map.add(formKey.impl(), WTFMove(state)); } if (i != stateVector.size()) map.clear(); }
void V8Window::namedPropertyGetterCustom(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) { LocalDOMWindow* window = V8Window::toNative(info.Holder()); if (!window) return; LocalFrame* frame = window->frame(); // window is detached from a frame. if (!frame) return; // Search sub-frames. AtomicString propName = toCoreAtomicString(name); Frame* child = frame->tree().scopedChild(propName); if (child) { v8SetReturnValueFast(info, child->domWindow(), window); return; } // Search IDL functions defined in the prototype if (!info.Holder()->GetRealNamedProperty(name).IsEmpty()) return; // Search named items in the document. Document* doc = frame->document(); if (doc && doc->isHTMLDocument()) { if (toHTMLDocument(doc)->hasNamedItem(propName) || doc->hasElementWithId(propName.impl())) { RefPtrWillBeRawPtr<HTMLCollection> items = doc->windowNamedItems(propName); if (!items->isEmpty()) { if (items->hasExactlyOneItem()) { v8SetReturnValueFast(info, items->item(0), window); return; } v8SetReturnValueFast(info, items.release(), window); return; } } } }
v8::Handle<v8::Value> V8DOMWindow::namedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS("DOM.DOMWindow.NamedPropertyGetter"); DOMWindow* window = V8DOMWindow::toNative(info.Holder()); if (!window) return notHandledByInterceptor(); Frame* frame = window->frame(); // window is detached from a frame. if (!frame) return notHandledByInterceptor(); // Search sub-frames. AtomicString propName = v8StringToAtomicWebCoreString(name); Frame* child = frame->tree()->child(propName); if (child) return toV8(child->domWindow()); // Search IDL functions defined in the prototype v8::Handle<v8::Value> result = info.Holder()->GetRealNamedProperty(name); if (!result.IsEmpty()) return result; // Search named items in the document. Document* doc = frame->document(); if (doc && doc->isHTMLDocument()) { if (static_cast<HTMLDocument*>(doc)->hasNamedItem(propName.impl()) || doc->hasElementWithId(propName.impl())) { RefPtr<HTMLCollection> items = doc->windowNamedItems(propName); if (items->length() >= 1) { if (items->length() == 1) return toV8(items->firstItem()); return toV8(items.release()); } } } return notHandledByInterceptor(); }
void RenderCounter::destroyCounterNode(RenderObject* owner, const AtomicString& identifier) { CounterMap* map = counterMaps().get(owner); if (!map) return; CounterMap::iterator mapIterator = map->find(identifier.impl()); if (mapIterator == map->end()) return; destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second.get()); map->remove(mapIterator); // We do not delete "map" here even if empty because we expect to reuse // it soon. In order for a renderer to lose all its counters permanently, // a style change for the renderer involving removal of all counter // directives must occur, in which case, RenderCounter::destroyCounterNodes() // must be called. // The destruction of the Renderer (possibly caused by the removal of its // associated DOM node) is the other case that leads to the permanent // destruction of all counters attached to a Renderer. In this case // RenderCounter::destroyCounterNodes() must be and is now called, too. // RenderCounter::destroyCounterNodes() handles destruction of the counter // map associated with a renderer, so there is no risk in leaking the map. }
static void removeItemFromMap(HashCountedSet<AtomicStringImpl*>& map, const AtomicString& name) { if (name.isEmpty()) return; map.remove(name.impl()); }
static void addItemToMap(HashCountedSet<AtomicStringImpl*>& map, const AtomicString& name) { if (name.isEmpty()) return; map.add(name.impl()); }
void BatteryProvider::didChangeBatteryStatus(const AtomicString& eventType, PassRefPtr<BatteryStatus> status) { WKRetainPtr<WKBatteryStatusRef> wkBatteryStatus = adoptWK(WKBatteryStatusCreate(status->charging(), status->chargingTime(), status->dischargingTime(), status->level())); WKBatteryManagerProviderDidChangeBatteryStatus(m_batteryManager.get(), toAPI(eventType.impl()), wkBatteryStatus.get()); }
CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name) { if (name.isNull()) return PseudoUnknown; HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap(); HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl()); return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second; }
static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) { ASSERT(object); // Real text nodes don't have their own style so they can't have counters. // We can't even look at their styles or we'll see extra resets and increments! if (object->isText() && !object->isBR()) return false; Node* generatingNode = object->generatingNode(); // We must have a generating node or else we cannot have a counter. if (!generatingNode) return false; RenderStyle* style = object->style(); ASSERT(style); switch (style->styleType()) { case NOPSEUDO: // Sometimes nodes have more then one renderer. Only the first one gets the counter // LayoutTests/http/tests/css/counter-crash.html if (generatingNode->renderer() != object) return false; break; case BEFORE: case AFTER: break; default: return false; // Counters are forbidden from all other pseudo elements. } if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { CounterDirectives directives = directivesMap->get(identifier.impl()); if (directives.m_reset) { value = directives.m_resetValue; if (directives.m_increment) value += directives.m_incrementValue; isReset = true; return true; } if (directives.m_increment) { value = directives.m_incrementValue; isReset = false; return true; } } if (identifier == "list-item") { if (object->isListItem()) { if (toRenderListItem(object)->hasExplicitValue()) { value = toRenderListItem(object)->explicitValue(); isReset = true; return true; } value = 1; isReset = false; return true; } if (Node* e = object->node()) { if (e->hasTagName(olTag)) { value = static_cast<HTMLOListElement*>(e)->start(); isReset = true; return true; } if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) { value = 0; isReset = true; return true; } } } return false; }
CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name) { if (name.isNull()) return PseudoUnknown; HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap(); HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl()); if (slot != nameToPseudoType->end()) return slot->value; if (name.startsWith("-webkit-")) return PseudoWebKitCustomElement; if (name.startsWith("x-") || name.startsWith("cue")) return PseudoUserAgentCustomElement; return PseudoUnknown; }
PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias) { if (alias.isEmpty() || !m_elementAliases) return 0; return m_elementAliases->get(alias.impl()); }
void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& attrName, const AtomicString& attrValue) { if (!mappedAttributeDecls) return; mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl())); }
void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl) { if (!mappedAttributeDecls) mappedAttributeDecls = new MappedAttributeDecls; mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), value.impl()), decl); }
void TreeScope::removeLabel(const AtomicString& forAttributeValue, HTMLLabelElement* element) { ASSERT(m_labelsByForAttribute); m_labelsByForAttribute->remove(forAttributeValue.impl(), element); }
static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction, Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions) { ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); // When the element is null, use its parent for scoping purposes. const Element* elementForScoping = element ? element : &parentElement; const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl()); if (!keyframesRule) return; const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes(); if (styleKeyframes.isEmpty()) return; // Construct and populate the style for each keyframe PropertySet specifiedProperties; KeyframeEffectModel::KeyframeVector keyframes; HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions; for (size_t i = 0; i < styleKeyframes.size(); ++i) { const StyleKeyframe* styleKeyframe = styleKeyframes[i].get(); // It's OK to pass a null element here. RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name); RefPtr<Keyframe> keyframe = Keyframe::create(); const Vector<double>& offsets = styleKeyframe->keys(); ASSERT(!offsets.isEmpty()); keyframe->setOffset(offsets[0]); TimingFunction* timingFunction = defaultTimingFunction; const StylePropertySet* properties = styleKeyframe->properties(); for (unsigned j = 0; j < properties->propertyCount(); j++) { CSSPropertyID property = properties->propertyAt(j).id(); specifiedProperties.add(property); if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) timingFunction = KeyframeValue::timingFunction(*keyframeStyle); else if (CSSAnimations::isAnimatableProperty(property)) keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get()); } keyframes.append(keyframe); // The last keyframe specified at a given offset is used. perKeyframeTimingFunctions.set(offsets[0], timingFunction); for (size_t j = 1; j < offsets.size(); ++j) { keyframes.append(keyframe->cloneWithOffset(offsets[j])); perKeyframeTimingFunctions.set(offsets[j], timingFunction); } } ASSERT(!keyframes.isEmpty()); if (!perKeyframeTimingFunctions.contains(0)) perKeyframeTimingFunctions.set(0, defaultTimingFunction); for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) { const CSSPropertyID property = *iter; ASSERT(property != CSSPropertyInvalid); blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property)); } // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority. std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets); size_t targetIndex = 0; for (size_t i = 1; i < keyframes.size(); i++) { if (keyframes[i]->offset() != keyframes[targetIndex]->offset()) targetIndex++; if (targetIndex != i) keyframes[targetIndex] = keyframes[i]; } keyframes.shrink(targetIndex + 1); // Add 0% and 100% keyframes if absent. RefPtr<Keyframe> startKeyframe = keyframes[0]; if (startKeyframe->offset()) { startKeyframe = Keyframe::create(); startKeyframe->setOffset(0); keyframes.prepend(startKeyframe); } RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1]; if (endKeyframe->offset() != 1) { endKeyframe = Keyframe::create(); endKeyframe->setOffset(1); keyframes.append(endKeyframe); } ASSERT(keyframes.size() >= 2); ASSERT(!keyframes.first()->offset()); ASSERT(keyframes.last()->offset() == 1); // Snapshot current property values for 0% and 100% if missing. PropertySet allProperties; size_t numKeyframes = keyframes.size(); for (size_t i = 0; i < numKeyframes; i++) { const PropertySet& keyframeProperties = keyframes[i]->properties(); for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter) allProperties.add(*iter); } const PropertySet& startKeyframeProperties = startKeyframe->properties(); const PropertySet& endKeyframeProperties = endKeyframe->properties(); bool missingStartValues = startKeyframeProperties.size() < allProperties.size(); bool missingEndValues = endKeyframeProperties.size() < allProperties.size(); if (missingStartValues || missingEndValues) { for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) { const CSSPropertyID property = *iter; bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property); bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property); if (!startNeedsValue && !endNeedsValue) continue; RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style); if (startNeedsValue) startKeyframe->setPropertyValue(property, snapshotValue.get()); if (endNeedsValue) endKeyframe->setPropertyValue(property, snapshotValue.get()); } } ASSERT(startKeyframe->properties().size() == allProperties.size()); ASSERT(endKeyframe->properties().size() == allProperties.size()); // Determine how many keyframes specify each property. Note that this must // be done after we've filled in end keyframes. typedef HashCountedSet<CSSPropertyID> PropertyCountedSet; PropertyCountedSet propertyCounts; for (size_t i = 0; i < numKeyframes; ++i) { const PropertySet& properties = keyframes[i]->properties(); for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter) propertyCounts.add(*iter); } // Split keyframes into groups, where each group contains only keyframes // which specify all properties used in that group. Each group is animated // in a separate animation, to allow per-keyframe timing functions to be // applied correctly. for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) { const CSSPropertyID property = iter->key; const size_t count = iter->value; ASSERT(count <= numKeyframes); if (count == numKeyframes) continue; KeyframeEffectModel::KeyframeVector splitOutKeyframes; for (size_t i = 0; i < numKeyframes; i++) { Keyframe* keyframe = keyframes[i].get(); if (!keyframe->properties().contains(property)) { ASSERT(i && i != numKeyframes - 1); continue; } RefPtr<Keyframe> clonedKeyframe = Keyframe::create(); clonedKeyframe->setOffset(keyframe->offset()); clonedKeyframe->setComposite(keyframe->composite()); clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property)); splitOutKeyframes.append(clonedKeyframe); // Note that it's OK if this keyframe ends up having no // properties. This can only happen when none of the properties // are specified in all keyframes, in which case we won't animate // anything with these keyframes. keyframe->clearPropertyValue(property); } ASSERT(!splitOutKeyframes.first()->offset()); ASSERT(splitOutKeyframes.last()->offset() == 1); #ifndef NDEBUG for (size_t j = 0; j < splitOutKeyframes.size(); ++j) ASSERT(splitOutKeyframes[j]->properties().size() == 1); #endif keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions))); } unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size(); #ifndef NDEBUG for (size_t i = 1; i < numKeyframes; ++i) ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes); #endif // If the animation specifies any keyframes, we always provide at least one // vector of resolved keyframes, even if no properties are animated. if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty()) keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions))); }