void MutationObserver::deliver() { ASSERT(!shouldBeSuspended()); // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary // to make a copy of the transient registrations before operating on them. WillBeHeapVector<RawPtrWillBeMember<MutationObserverRegistration>, 1> transientRegistrations; for (auto& registration : m_registrations) { if (registration->hasTransientRegistrations()) transientRegistrations.append(registration); } for (size_t i = 0; i < transientRegistrations.size(); ++i) transientRegistrations[i]->clearTransientRegistrations(); if (m_records.isEmpty()) return; MutationRecordVector records; records.swap(m_records); InspectorInstrumentation::willDeliverMutationRecords(m_callback->executionContext(), this); m_callback->call(records, this); InspectorInstrumentation::didDeliverMutationRecords(m_callback->executionContext()); }
static v8::Handle<v8::Value> getNamedItems(HTMLAllCollection* collection, AtomicString name, const CallbackInfo& info) { WillBeHeapVector<RefPtrWillBeMember<Element> > namedItems; collection->namedItems(name, namedItems); if (!namedItems.size()) return v8Undefined(); if (namedItems.size() == 1) return toV8(namedItems.at(0).release(), info.Holder(), info.GetIsolate()); // FIXME: HTML5 specification says this should be a HTMLCollection. // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#htmlallcollection // // FIXME: Oilpan: explicitly convert adopt()'s result so as to // disambiguate the (implicit) conversion of its // PassRefPtrWillBeRawPtr<StaticElementList> result -- the // other toV8() overload that introduces the ambiguity is // toV8(NodeList*, ...). // // When adopt() no longer uses transition types, the conversion // can be removed. return toV8(PassRefPtrWillBeRawPtr<NodeList>(StaticElementList::adopt(namedItems)), info.Holder(), info.GetIsolate()); }
bool AnimatableRepeatable::interpolateLists(const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& fromValues, const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& toValues, double fraction, WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >& interpolatedValues) { // Interpolation behaviour spec: http://www.w3.org/TR/css3-transitions/#animtype-repeatable-list ASSERT(interpolatedValues.isEmpty()); ASSERT(!fromValues.isEmpty() && !toValues.isEmpty()); size_t size = lowestCommonMultiple(fromValues.size(), toValues.size()); ASSERT(size > 0); for (size_t i = 0; i < size; ++i) { const AnimatableValue* from = fromValues[i % fromValues.size()].get(); const AnimatableValue* to = toValues[i % toValues.size()].get(); // Spec: If a pair of values cannot be interpolated, then the lists are not interpolable. if (AnimatableValue::usesDefaultInterpolation(from, to)) return false; interpolatedValues.append(interpolate(from, to, fraction)); } return true; }
bool TreeScopeStyleSheetCollection::activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet>>& newStyleSheets) { // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done. bool hasActiveLoadingStylesheet = false; unsigned newStylesheetCount = newStyleSheets.size(); for (unsigned i = 0; i < newStylesheetCount; ++i) { if (newStyleSheets[i]->isLoading()) hasActiveLoadingStylesheet = true; } if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) { m_hadActiveLoadingStylesheet = false; return true; } m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet; return false; }
void collectDestinationInsertionPoints(const Node& node, WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8>& results) { const Node* current = &node; ElementShadow* lastElementShadow = 0; while (true) { ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current); if (!shadow || shadow == lastElementShadow) return; lastElementShadow = shadow; const DestinationInsertionPoints* insertionPoints = shadow->destinationInsertionPointsFor(&node); if (!insertionPoints) return; for (size_t i = 0; i < insertionPoints->size(); ++i) results.append(insertionPoints->at(i).get()); ASSERT(current != insertionPoints->last().get()); current = insertionPoints->last().get(); } }
void EventPath::adjustTouchList(const TouchList* touchList, WillBeHeapVector<RawPtrWillBeMember<TouchList>> adjustedTouchList, const WillBeHeapVector<RawPtrWillBeMember<TreeScope>>& treeScopes) { if (!touchList) return; for (size_t i = 0; i < touchList->length(); ++i) { const Touch& touch = *touchList->item(i); if (!touch.target()) continue; Node* targetNode = touch.target()->toNode(); if (!targetNode) continue; RelatedTargetMap relatedNodeMap; buildRelatedNodeMap(*targetNode, relatedNodeMap); for (size_t j = 0; j < treeScopes.size(); ++j) { adjustedTouchList[j]->append(touch.cloneWithNewTarget(findRelatedNode(*treeScopes[j], relatedNodeMap))); } } }
void IdTargetObserverRegistry::notifyObserversInternal(const AtomicString& id) { ASSERT(!id.isEmpty()); ASSERT(!m_registry.isEmpty()); m_notifyingObserversInSet = m_registry.get(id.impl()); if (!m_notifyingObserversInSet) return; WillBeHeapVector<RawPtrWillBeMember<IdTargetObserver> > copy; copyToVector(*m_notifyingObserversInSet, copy); for (WillBeHeapVector<RawPtrWillBeMember<IdTargetObserver> >::const_iterator it = copy.begin(); it != copy.end(); ++it) { if (m_notifyingObserversInSet->contains(*it)) (*it)->idTargetChanged(); } if (m_notifyingObserversInSet->isEmpty()) m_registry.remove(id.impl()); m_notifyingObserversInSet = nullptr; }
void HTMLFormElement::anonymousNamedGetter(const AtomicString& name, RadioNodeListOrElement& returnValue) { // Call getNamedElements twice, first time check if it has a value // and let HTMLFormElement update its cache. // See issue: 867404 { WillBeHeapVector<RefPtrWillBeMember<Element>> elements; getNamedElements(name, elements); if (elements.isEmpty()) return; } // Second call may return different results from the first call, // but if the first the size cannot be zero. WillBeHeapVector<RefPtrWillBeMember<Element>> elements; getNamedElements(name, elements); ASSERT(!elements.isEmpty()); bool onlyMatchImg = !elements.isEmpty() && isHTMLImageElement(*elements.first()); if (onlyMatchImg) { UseCounter::count(document(), UseCounter::FormNameAccessForImageElement); // The following code has performance impact, but it should be small // because <img> access via <form> name getter is rarely used. for (auto& element : elements) { if (isHTMLImageElement(*element) && !element->isDescendantOf(this)) { UseCounter::count(document(), UseCounter::FormNameAccessForNonDescendantImageElement); break; } } } if (elements.size() == 1) { returnValue.setElement(elements.at(0)); return; } returnValue.setRadioNodeList(radioNodeList(name, onlyMatchImg)); }
int SimplifyMarkupCommand::pruneSubsequentAncestorsToRemove(WillBeHeapVector<RefPtrWillBeMember<ContainerNode>>& nodesToRemove, size_t startNodeIndex) { size_t pastLastNodeToRemove = startNodeIndex + 1; for (; pastLastNodeToRemove < nodesToRemove.size(); ++pastLastNodeToRemove) { if (nodesToRemove[pastLastNodeToRemove - 1]->parentNode() != nodesToRemove[pastLastNodeToRemove]) break; ASSERT(nodesToRemove[pastLastNodeToRemove]->firstChild() == nodesToRemove[pastLastNodeToRemove]->lastChild()); } ContainerNode* highestAncestorToRemove = nodesToRemove[pastLastNodeToRemove - 1].get(); RefPtrWillBeRawPtr<ContainerNode> parent = highestAncestorToRemove->parentNode(); if (!parent) // Parent has already been removed. return -1; if (pastLastNodeToRemove == startNodeIndex + 1) return 0; removeNode(nodesToRemove[startNodeIndex], AssumeContentIsAlwaysEditable); insertNodeBefore(nodesToRemove[startNodeIndex], highestAncestorToRemove, AssumeContentIsAlwaysEditable); removeNode(highestAncestorToRemove, AssumeContentIsAlwaysEditable); return pastLastNodeToRemove - startNodeIndex - 1; }
Element* HTMLCollection::namedItem(const AtomicString& name) const { // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp // This method first searches for an object with a matching id // attribute. If a match is not found, the method then searches for an // object with a matching name attribute, but only on those elements // that are allowed a name attribute. updateIdNameCache(); const NamedItemCache& cache = namedItemCache(); WillBeHeapVector<RawPtrWillBeMember<Element>>* idResults = cache.getElementsById(name); if (idResults && !idResults->isEmpty()) return idResults->first(); WillBeHeapVector<RawPtrWillBeMember<Element>>* nameResults = cache.getElementsByName(name); if (nameResults && !nameResults->isEmpty()) return nameResults->first(); return nullptr; }
void PageAnimator::serviceScriptedAnimations(double monotonicAnimationStartTime) { RefPtrWillBeRawPtr<PageAnimator> protector(this); m_animationFramePending = false; TemporaryChange<bool> servicing(m_servicingAnimations, true); WillBeHeapVector<RefPtrWillBeMember<Document>> documents; for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { if (frame->isLocalFrame()) documents.append(toLocalFrame(frame)->document()); } for (size_t i = 0; i < documents.size(); ++i) { if (documents[i]->frame()) { documents[i]->view()->serviceScrollAnimations(monotonicAnimationStartTime); if (const FrameView::ScrollableAreaSet* animatingScrollableAreas = documents[i]->view()->animatingScrollableAreas()) { // Iterate over a copy, since ScrollableAreas may deregister // themselves during the iteration. Vector<ScrollableArea*> animatingScrollableAreasCopy; copyToVector(*animatingScrollableAreas, animatingScrollableAreasCopy); for (ScrollableArea* scrollableArea : animatingScrollableAreasCopy) scrollableArea->serviceScrollAnimations(monotonicAnimationStartTime); } } } for (size_t i = 0; i < documents.size(); ++i) { DocumentAnimations::updateAnimationTimingForAnimationFrame(*documents[i], monotonicAnimationStartTime); SVGDocumentExtensions::serviceOnAnimationFrame(*documents[i], monotonicAnimationStartTime); } for (size_t i = 0; i < documents.size(); ++i) documents[i]->serviceScriptedAnimations(monotonicAnimationStartTime); #if ENABLE(OILPAN) documents.clear(); #endif }
inline void DistributionPool::clear() { detachNonDistributedNodes(); m_nodes.clear(); m_distributed.clear(); }
void EventPath::calculatePath() { ASSERT(m_node); ASSERT(m_nodeEventContexts.isEmpty()); m_node->updateDistribution(); // For performance and memory usage reasons we want to store the // path using as few bytes as possible and with as few allocations // as possible which is why we gather the data on the stack before // storing it in a perfectly sized m_nodeEventContexts Vector. WillBeHeapVector<RawPtrWillBeMember<Node>, 64> nodesInPath; Node* current = m_node; nodesInPath.append(current); while (current) { if (m_event && current->keepEventInNode(m_event)) break; WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; collectDestinationInsertionPoints(*current, insertionPoints); if (!insertionPoints.isEmpty()) { for (const auto& insertionPoint : insertionPoints) { if (insertionPoint->isShadowInsertionPoint()) { ShadowRoot* containingShadowRoot = insertionPoint->containingShadowRoot(); ASSERT(containingShadowRoot); if (!containingShadowRoot->isOldest()) nodesInPath.append(containingShadowRoot->olderShadowRoot()); } nodesInPath.append(insertionPoint); } current = insertionPoints.last(); continue; } if (current->isShadowRoot()) { if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(current), *m_node)) break; current = current->shadowHost(); #if !ENABLE(OILPAN) // TODO(kochi): crbug.com/507413 This check is necessary when some asynchronous event // is queued while its shadow host is removed and the shadow root gets the event // immediately after it. When Oilpan is enabled, this situation does not happen. // Except this case, shadow root's host is assumed to be non-null. if (current) nodesInPath.append(current); #else nodesInPath.append(current); #endif } else { current = current->parentNode(); if (current) nodesInPath.append(current); } } m_nodeEventContexts.reserveCapacity(nodesInPath.size()); for (Node* nodeInPath : nodesInPath) { m_nodeEventContexts.append(NodeEventContext(nodeInPath, eventTargetRespectingTargetRules(*nodeInPath))); } }
void AutomaticTrackSelection::performAutomaticTextTrackSelection(const TrackGroup& group) { ASSERT(group.tracks.size()); // First, find the track in the group that should be enabled (if any). WillBeHeapVector<RefPtrWillBeMember<TextTrack>> currentlyEnabledTracks; RefPtrWillBeRawPtr<TextTrack> trackToEnable = nullptr; RefPtrWillBeRawPtr<TextTrack> defaultTrack = nullptr; RefPtrWillBeRawPtr<TextTrack> preferredTrack = nullptr; RefPtrWillBeRawPtr<TextTrack> fallbackTrack = nullptr; int highestTrackScore = 0; for (size_t i = 0; i < group.tracks.size(); ++i) { RefPtrWillBeRawPtr<TextTrack> textTrack = group.tracks[i]; if (m_configuration.disableCurrentlyEnabledTracks && textTrack->mode() == TextTrack::showingKeyword()) currentlyEnabledTracks.append(textTrack); int trackScore = textTrackSelectionScore(*textTrack); if (textTrack->kind() == preferredTrackKind()) trackScore += 1; if (trackScore) { // * If the text track kind is subtitles or captions and the user has indicated an interest in having a // track with this text track kind, text track language, and text track label enabled, and there is no // other text track in the media element's list of text tracks with a text track kind of either subtitles // or captions whose text track mode is showing // Let the text track mode be showing. if (trackScore > highestTrackScore) { preferredTrack = textTrack; highestTrackScore = trackScore; } if (!defaultTrack && textTrack->isDefault()) defaultTrack = textTrack; if (!fallbackTrack) fallbackTrack = textTrack; } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) { // * If the track element has a default attribute specified, and there is no other text track in the media // element's list of text tracks whose text track mode is showing or showing by default // Let the text track mode be showing by default. defaultTrack = textTrack; } } if (m_configuration.textTrackKindUserPreference != TextTrackKindUserPreference::Default) trackToEnable = preferredTrack; if (!trackToEnable && defaultTrack) trackToEnable = defaultTrack; if (!trackToEnable && m_configuration.forceEnableSubtitleOrCaptionTrack && group.kind == TrackGroup::CaptionsAndSubtitles) trackToEnable = fallbackTrack ? fallbackTrack : group.tracks[0]; if (currentlyEnabledTracks.size()) { for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) { RefPtrWillBeRawPtr<TextTrack> textTrack = currentlyEnabledTracks[i]; if (textTrack != trackToEnable) textTrack->setMode(TextTrack::disabledKeyword()); } } if (trackToEnable) trackToEnable->setMode(TextTrack::showingKeyword()); }
void BreakBlockquoteCommand::doApply() { if (endingSelection().isNone()) return; // Delete the current selection. if (endingSelection().isRange()) deleteSelection(false, false); // This is a scenario that should never happen, but we want to // make sure we don't dereference a null pointer below. ASSERT(!endingSelection().isNone()); if (endingSelection().isNone()) return; VisiblePosition visiblePos = endingSelection().visibleStart(); // pos is a position equivalent to the caret. We use downstream() so that pos will // be in the first node that we need to move (there are a few exceptions to this, see below). Position pos = mostForwardCaretPosition(endingSelection().start()); // Find the top-most blockquote from the start. HTMLQuoteElement* topBlockquote = toHTMLQuoteElement(highestEnclosingNodeOfType(pos, isMailHTMLBlockquoteElement)); if (!topBlockquote || !topBlockquote->parentNode()) return; RefPtrWillBeRawPtr<HTMLBRElement> breakElement = createBreakElement(document()); bool isLastVisPosInNode = isLastVisiblePositionInNode(visiblePos, topBlockquote); // If the position is at the beginning of the top quoted content, we don't need to break the quote. // Instead, insert the break before the blockquote, unless the position is as the end of the the quoted content. if (isFirstVisiblePositionInNode(visiblePos, topBlockquote) && !isLastVisPosInNode) { insertNodeBefore(breakElement.get(), topBlockquote); setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional())); rebalanceWhitespace(); return; } // Insert a break after the top blockquote. insertNodeAfter(breakElement.get(), topBlockquote); // If we're inserting the break at the end of the quoted content, we don't need to break the quote. if (isLastVisPosInNode) { setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional())); rebalanceWhitespace(); return; } // Don't move a line break just after the caret. Doing so would create an extra, empty paragraph // in the new blockquote. if (lineBreakExistsAtVisiblePosition(visiblePos)) { // TODO(yosin) We should use |PositionMoveType::Character| for // |nextPositionOf()| to avoid editing middle of character. pos = nextPositionOf(pos, PositionMoveType::CodePoint); } // Adjust the position so we don't split at the beginning of a quote. while (isFirstVisiblePositionInNode(createVisiblePosition(pos), toHTMLQuoteElement(enclosingNodeOfType(pos, isMailHTMLBlockquoteElement)))) { // TODO(yosin) We should use |PositionMoveType::Character| for // |previousPositionOf()| to avoid editing middle character. pos = previousPositionOf(pos, PositionMoveType::CodePoint); } // startNode is the first node that we need to move to the new blockquote. Node* startNode = pos.anchorNode(); ASSERT(startNode); // Split at pos if in the middle of a text node. if (startNode->isTextNode()) { Text* textNode = toText(startNode); int textOffset = pos.computeOffsetInContainerNode(); if ((unsigned)textOffset >= textNode->length()) { startNode = NodeTraversal::next(*startNode); ASSERT(startNode); } else if (textOffset > 0) { splitTextNode(textNode, textOffset); } } else if (pos.computeEditingOffset() > 0) { Node* childAtOffset = NodeTraversal::childAt(*startNode, pos.computeEditingOffset()); startNode = childAtOffset ? childAtOffset : NodeTraversal::next(*startNode); ASSERT(startNode); } // If there's nothing inside topBlockquote to move, we're finished. if (!startNode->isDescendantOf(topBlockquote)) { setEndingSelection(VisibleSelection(createVisiblePosition(firstPositionInOrBeforeNode(startNode)), endingSelection().isDirectional())); return; } // Build up list of ancestors in between the start node and the top blockquote. WillBeHeapVector<RefPtrWillBeMember<Element>> ancestors; for (Element* node = startNode->parentElement(); node && node != topBlockquote; node = node->parentElement()) ancestors.append(node); // Insert a clone of the top blockquote after the break. RefPtrWillBeRawPtr<Element> clonedBlockquote = topBlockquote->cloneElementWithoutChildren(); insertNodeAfter(clonedBlockquote.get(), breakElement.get()); // Clone startNode's ancestors into the cloned blockquote. // On exiting this loop, clonedAncestor is the lowest ancestor // that was cloned (i.e. the clone of either ancestors.last() // or clonedBlockquote if ancestors is empty). RefPtrWillBeRawPtr<Element> clonedAncestor = clonedBlockquote; for (size_t i = ancestors.size(); i != 0; --i) { RefPtrWillBeRawPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren(); // Preserve list item numbering in cloned lists. if (isHTMLOListElement(*clonedChild)) { Node* listChildNode = i > 1 ? ancestors[i - 2].get() : startNode; // The first child of the cloned list might not be a list item element, // find the first one so that we know where to start numbering. while (listChildNode && !isHTMLLIElement(*listChildNode)) listChildNode = listChildNode->nextSibling(); if (isListItem(listChildNode)) setNodeAttribute(clonedChild, startAttr, AtomicString::number(toLayoutListItem(listChildNode->layoutObject())->value())); } appendNode(clonedChild.get(), clonedAncestor.get()); clonedAncestor = clonedChild; } moveRemainingSiblingsToNewParent(startNode, 0, clonedAncestor); if (!ancestors.isEmpty()) { // Split the tree up the ancestor chain until the topBlockquote // Throughout this loop, clonedParent is the clone of ancestor's parent. // This is so we can clone ancestor's siblings and place the clones // into the clone corresponding to the ancestor's parent. RefPtrWillBeRawPtr<Element> ancestor = nullptr; RefPtrWillBeRawPtr<Element> clonedParent = nullptr; for (ancestor = ancestors.first(), clonedParent = clonedAncestor->parentElement(); ancestor && ancestor != topBlockquote; ancestor = ancestor->parentElement(), clonedParent = clonedParent->parentElement()) moveRemainingSiblingsToNewParent(ancestor->nextSibling(), 0, clonedParent); // If the startNode's original parent is now empty, remove it Element* originalParent = ancestors.first().get(); if (!originalParent->hasChildren()) removeNode(originalParent); } // Make sure the cloned block quote renders. addBlockPlaceholderIfNeeded(clonedBlockquote.get()); // Put the selection right before the break. setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional())); rebalanceWhitespace(); }
StyleSheetInvalidationAnalysis::StyleSheetInvalidationAnalysis(const TreeScope& treeScope, const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>& sheets) : m_treeScope(&treeScope) { for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i) analyzeStyleSheet(sheets[i]); }
void ScopedStyleResolver::addKeyframeRules(const RuleSet& ruleSet) { const WillBeHeapVector<RawPtrWillBeMember<StyleRuleKeyframes>> keyframesRules = ruleSet.keyframesRules(); for (unsigned i = 0; i < keyframesRules.size(); ++i) addKeyframeStyle(keyframesRules[i]); }
bool IDBRequest::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) { IDB_TRACE("IDBRequest::dispatchEvent"); if (m_contextStopped || !executionContext()) return false; ASSERT(m_readyState == PENDING); ASSERT(m_hasPendingActivity); ASSERT(m_enqueuedEvents.size()); ASSERT(event->target() == this); ScriptState::Scope scope(m_scriptState.get()); if (event->type() != EventTypeNames::blocked) m_readyState = DONE; dequeueEvent(event.get()); WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets; targets.append(this); if (m_transaction && !m_preventPropagation) { targets.append(m_transaction); // If there ever are events that are associated with a database but // that do not have a transaction, then this will not work and we need // this object to actually hold a reference to the database (to ensure // it stays alive). targets.append(m_transaction->db()); } // Cursor properties should not be updated until the success event is being dispatched. IDBCursor* cursorToNotify = 0; if (event->type() == EventTypeNames::success) { cursorToNotify = getResultCursor(); if (cursorToNotify) { if (m_blobInfo && m_blobInfo->size() > 0) V8PerIsolateData::from(scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->unregisterRequest(*this); cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release(), m_blobInfo.release()); } } if (event->type() == EventTypeNames::upgradeneeded) { ASSERT(!m_didFireUpgradeNeededEvent); m_didFireUpgradeNeededEvent = true; } // FIXME: When we allow custom event dispatching, this will probably need to change. ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type() == EventTypeNames::error || event->type() == EventTypeNames::blocked || event->type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().utf8().data()); const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted)); if (setTransactionActive) m_transaction->setActive(true); bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets); if (m_transaction) { if (m_readyState == DONE) m_transaction->unregisterRequest(this); // Possibly abort the transaction. This must occur after unregistering (so this request // doesn't receive a second error) and before deactivating (which might trigger commit). if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) { m_transaction->setError(m_error); m_transaction->abort(IGNORE_EXCEPTION); } // If this was the last request in the transaction's list, it may commit here. if (setTransactionActive) m_transaction->setActive(false); } if (cursorToNotify) cursorToNotify->postSuccessHandlerCallback(); // An upgradeneeded event will always be followed by a success or error event, so must // be kept alive. if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded) m_hasPendingActivity = false; return dontPreventDefault; }
void MutableStylePropertySet::addParsedProperties(const WillBeHeapVector<CSSProperty, 256>& properties) { m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size()); for (unsigned i = 0; i < properties.size(); ++i) addParsedProperty(properties[i]); }
const TreeScope* TreeScope::commonAncestorTreeScope(const TreeScope& other) const { WillBeHeapVector<RawPtrWillBeMember<const TreeScope>, 16> thisChain; for (const TreeScope* tree = this; tree; tree = tree->parentTreeScope()) thisChain.append(tree); WillBeHeapVector<RawPtrWillBeMember<const TreeScope>, 16> otherChain; for (const TreeScope* tree = &other; tree; tree = tree->parentTreeScope()) otherChain.append(tree); // Keep popping out the last elements of these chains until a mismatched pair is found. If |this| and |other| // belong to different documents, null will be returned. const TreeScope* lastAncestor = nullptr; while (!thisChain.isEmpty() && !otherChain.isEmpty() && thisChain.last() == otherChain.last()) { lastAncestor = thisChain.last(); thisChain.removeLast(); otherChain.removeLast(); } return lastAncestor; }
void run(LocalFrame* frame) override { if (m_running) return; m_running = true; // 0. Flush pending frontend messages. WebLocalFrameImpl* frameImpl = WebLocalFrameImpl::fromFrame(frame); WebDevToolsAgentImpl* agent = frameImpl->devToolsAgentImpl(); agent->flushPendingProtocolNotifications(); Vector<WebViewImpl*> views; WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>> widgets; // 1. Disable input events. const HashSet<WebViewImpl*>& viewImpls = WebViewImpl::allInstances(); HashSet<WebViewImpl*>::const_iterator viewImplsEnd = viewImpls.end(); for (HashSet<WebViewImpl*>::const_iterator it = viewImpls.begin(); it != viewImplsEnd; ++it) { WebViewImpl* view = *it; m_frozenViews.add(view); views.append(view); view->setIgnoreInputEvents(true); } const WebFrameWidgetsSet& widgetImpls = WebFrameWidgetImpl::allInstances(); WebFrameWidgetsSet::const_iterator widgetImplsEnd = widgetImpls.end(); for (WebFrameWidgetsSet::const_iterator it = widgetImpls.begin(); it != widgetImplsEnd; ++it) { WebFrameWidgetImpl* widget = *it; m_frozenWidgets.add(widget); widgets.append(widget); widget->setIgnoreInputEvents(true); } // 2. Notify embedder about pausing. agent->client()->willEnterDebugLoop(); // 3. Disable active objects WebView::willEnterModalLoop(); // 4. Process messages until quitNow is called. m_messageLoop->run(); // 5. Resume active objects WebView::didExitModalLoop(); // 6. Resume input events. for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) { if (m_frozenViews.contains(*it)) { // The view was not closed during the dispatch. (*it)->setIgnoreInputEvents(false); } } for (WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>>::iterator it = widgets.begin(); it != widgets.end(); ++it) { if (m_frozenWidgets.contains(*it)) { // The widget was not closed during the dispatch. (*it)->setIgnoreInputEvents(false); } } // 7. Notify embedder about resuming. agent->client()->didExitDebugLoop(); // 8. All views have been resumed, clear the set. m_frozenViews.clear(); m_frozenWidgets.clear(); m_running = false; }
void HTMLFormattingElementList::ensureNoahsArkCondition(HTMLStackItem* newItem) { WillBeHeapVector<RawPtrWillBeMember<HTMLStackItem> > candidates; tryToEnsureNoahsArkConditionQuickly(newItem, candidates); if (candidates.isEmpty()) return; // We pre-allocate and re-use this second vector to save one malloc per // attribute that we verify. WillBeHeapVector<RawPtrWillBeMember<HTMLStackItem> > remainingCandidates; remainingCandidates.reserveInitialCapacity(candidates.size()); const Vector<Attribute>& attributes = newItem->attributes(); for (size_t i = 0; i < attributes.size(); ++i) { const Attribute& attribute = attributes[i]; for (size_t j = 0; j < candidates.size(); ++j) { HTMLStackItem* candidate = candidates[j]; // These properties should already have been checked by tryToEnsureNoahsArkConditionQuickly. ASSERT(newItem->attributes().size() == candidate->attributes().size()); ASSERT(newItem->localName() == candidate->localName() && newItem->namespaceURI() == candidate->namespaceURI()); Attribute* candidateAttribute = candidate->getAttributeItem(attribute.name()); if (candidateAttribute && candidateAttribute->value() == attribute.value()) remainingCandidates.append(candidate); } if (remainingCandidates.size() < kNoahsArkCapacity) return; candidates.swap(remainingCandidates); remainingCandidates.shrink(0); } // Inductively, we shouldn't spin this loop very many times. It's possible, // however, that we wil spin the loop more than once because of how the // formatting element list gets permuted. for (size_t i = kNoahsArkCapacity - 1; i < candidates.size(); ++i) remove(candidates[i]->element()); }
PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::put(ExecutionContext* executionContext, WebIDBDatabase::PutMode putMode, PassRefPtrWillBeRawPtr<IDBAny> source, ScriptValue& value, PassRefPtrWillBeRawPtr<IDBKey> prpKey, ExceptionState& exceptionState) { RefPtrWillBeRawPtr<IDBKey> key = prpKey; if (isDeleted()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage); return nullptr; } if (m_transaction->isFinished() || m_transaction->isFinishing()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); return nullptr; } if (!m_transaction->isActive()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); return nullptr; } if (m_transaction->isReadOnly()) { exceptionState.throwDOMException(ReadOnlyError, IDBDatabase::transactionReadOnlyErrorMessage); return nullptr; } Vector<WebBlobInfo> blobInfo; RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(value, &blobInfo, exceptionState, toIsolate(executionContext)); if (exceptionState.hadException()) return nullptr; if (serializedValue->containsBlobs()) { // FIXME: Add Blob/File/FileList support exceptionState.throwDOMException(DataCloneError, "The object store currently does not support blob values."); return nullptr; } ASSERT(blobInfo.isEmpty()); const IDBKeyPath& keyPath = m_metadata.keyPath; const bool usesInLineKeys = !keyPath.isNull(); const bool hasKeyGenerator = autoIncrement(); if (putMode != WebIDBDatabase::CursorUpdate && usesInLineKeys && key) { exceptionState.throwDOMException(DataError, "The object store uses in-line keys and the key parameter was provided."); return nullptr; } if (!usesInLineKeys && !hasKeyGenerator && !key) { exceptionState.throwDOMException(DataError, "The object store uses out-of-line keys and has no key generator and the key parameter was not provided."); return nullptr; } if (usesInLineKeys) { RefPtrWillBeRawPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(toIsolate(executionContext), value, keyPath); if (keyPathKey && !keyPathKey->isValid()) { exceptionState.throwDOMException(DataError, "Evaluating the object store's key path yielded a value that is not a valid key."); return nullptr; } if (!hasKeyGenerator && !keyPathKey) { exceptionState.throwDOMException(DataError, "Evaluating the object store's key path did not yield a value."); return nullptr; } if (hasKeyGenerator && !keyPathKey) { if (!canInjectIDBKeyIntoScriptValue(toIsolate(executionContext), value, keyPath)) { exceptionState.throwDOMException(DataError, "A generated key could not be inserted into the value."); return nullptr; } } if (keyPathKey) key = keyPathKey; } if (key && !key->isValid()) { exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage); return nullptr; } if (!backendDB()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage); return nullptr; } Vector<int64_t> indexIds; WillBeHeapVector<IndexKeys> indexKeys; for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) { IndexKeys keys; generateIndexKeysForValue(toIsolate(executionContext), it->value, value, &keys); indexIds.append(it->key); indexKeys.append(keys); } RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(executionContext, source, m_transaction.get()); Vector<char> wireBytes; serializedValue->toWireBytes(wireBytes); RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(wireBytes); backendDB()->put(m_transaction->id(), id(), blink::WebData(valueBuffer), blobInfo, key.release(), static_cast<WebIDBDatabase::PutMode>(putMode), WebIDBCallbacksImpl::create(request).leakPtr(), indexIds, indexKeys); return request.release(); }
void CueTimeline::updateActiveCues(double movieTime) { // 4.8.10.8 Playing the media resource // If the current playback position changes while the steps are running, // then the user agent must wait for the steps to complete, and then must // immediately rerun the steps. if (ignoreUpdateRequests()) return; HTMLMediaElement& mediaElement = this->mediaElement(); // https://html.spec.whatwg.org/#time-marches-on // 1 - Let current cues be a list of cues, initialized to contain all the // cues of all the hidden, showing, or showing by default text tracks of the // media element (not the disabled ones) whose start times are less than or // equal to the current playback position and whose end times are greater // than the current playback position. CueList currentCues; // The user agent must synchronously unset [the text track cue active] flag // whenever ... the media element's readyState is changed back to HAVE_NOTHING. if (mediaElement.readyState() != HTMLMediaElement::HAVE_NOTHING && mediaElement.webMediaPlayer()) currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime)); CueList previousCues; CueList missedCues; // 2 - Let other cues be a list of cues, initialized to contain all the cues // of hidden, showing, and showing by default text tracks of the media // element that are not present in current cues. previousCues = m_currentlyActiveCues; // 3 - Let last time be the current playback position at the time this // algorithm was last run for this media element, if this is not the first // time it has run. double lastTime = m_lastUpdateTime; double lastSeekTime = mediaElement.lastSeekTime(); // 4 - If the current playback position has, since the last time this // algorithm was run, only changed through its usual monotonic increase // during normal playback, then let missed cues be the list of cues in other // cues whose start times are greater than or equal to last time and whose // end times are less than or equal to the current playback position. // Otherwise, let missed cues be an empty list. if (lastTime >= 0 && lastSeekTime < movieTime) { CueList potentiallySkippedCues = m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime)); for (CueInterval cue : potentiallySkippedCues) { // Consider cues that may have been missed since the last seek time. if (cue.low() > std::max(lastSeekTime, lastTime) && cue.high() < movieTime) missedCues.append(cue); } } m_lastUpdateTime = movieTime; // 5 - If the time was reached through the usual monotonic increase of the // current playback position during normal playback, and if the user agent // has not fired a timeupdate event at the element in the past 15 to 250ms // and is not still running event handlers for such an event, then the user // agent must queue a task to fire a simple event named timeupdate at the // element. (In the other cases, such as explicit seeks, relevant events get // fired as part of the overall process of changing the current playback // position.) if (!mediaElement.seeking() && lastSeekTime < lastTime) mediaElement.scheduleTimeupdateEvent(true); // Explicitly cache vector sizes, as their content is constant from here. size_t currentCuesSize = currentCues.size(); size_t missedCuesSize = missedCues.size(); size_t previousCuesSize = previousCues.size(); // 6 - If all of the cues in current cues have their text track cue active // flag set, none of the cues in other cues have their text track cue active // flag set, and missed cues is empty, then abort these steps. bool activeSetChanged = missedCuesSize; for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) { if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive()) activeSetChanged = true; } for (CueInterval currentCue : currentCues) { // Notify any cues that are already active of the current time to mark // past and future nodes. Any inactive cues have an empty display state; // they will be notified of the current time when the display state is // updated. if (currentCue.data()->isActive()) currentCue.data()->updatePastAndFutureNodes(movieTime); else activeSetChanged = true; } if (!activeSetChanged) return; // 7 - If the time was reached through the usual monotonic increase of the // current playback position during normal playback, and there are cues in // other cues that have their text track cue pause-on-exi flag set and that // either have their text track cue active flag set or are also in missed // cues, then immediately pause the media element. for (size_t i = 0; !mediaElement.paused() && i < previousCuesSize; ++i) { if (previousCues[i].data()->pauseOnExit() && previousCues[i].data()->isActive() && !currentCues.contains(previousCues[i])) mediaElement.pause(); } for (size_t i = 0; !mediaElement.paused() && i < missedCuesSize; ++i) { if (missedCues[i].data()->pauseOnExit()) mediaElement.pause(); } // 8 - Let events be a list of tasks, initially empty. Each task in this // list will be associated with a text track, a text track cue, and a time, // which are used to sort the list before the tasks are queued. WillBeHeapVector<std::pair<double, RawPtrWillBeMember<TextTrackCue>>> eventTasks; // 8 - Let affected tracks be a list of text tracks, initially empty. WillBeHeapVector<RawPtrWillBeMember<TextTrack>> affectedTracks; for (size_t i = 0; i < missedCuesSize; ++i) { // 9 - For each text track cue in missed cues, prepare an event named enter // for the TextTrackCue object with the text track cue start time. eventTasks.append(std::make_pair(missedCues[i].data()->startTime(), missedCues[i].data())); // 10 - For each text track [...] in missed cues, prepare an event // named exit for the TextTrackCue object with the with the later of // the text track cue end time and the text track cue start time. // Note: An explicit task is added only if the cue is NOT a zero or // negative length cue. Otherwise, the need for an exit event is // checked when these tasks are actually queued below. This doesn't // affect sorting events before dispatch either, because the exit // event has the same time as the enter event. if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime()) eventTasks.append(std::make_pair(missedCues[i].data()->endTime(), missedCues[i].data())); } for (size_t i = 0; i < previousCuesSize; ++i) { // 10 - For each text track cue in other cues that has its text // track cue active flag set prepare an event named exit for the // TextTrackCue object with the text track cue end time. if (!currentCues.contains(previousCues[i])) eventTasks.append(std::make_pair(previousCues[i].data()->endTime(), previousCues[i].data())); } for (size_t i = 0; i < currentCuesSize; ++i) { // 11 - For each text track cue in current cues that does not have its // text track cue active flag set, prepare an event named enter for the // TextTrackCue object with the text track cue start time. if (!previousCues.contains(currentCues[i])) eventTasks.append(std::make_pair(currentCues[i].data()->startTime(), currentCues[i].data())); } // 12 - Sort the tasks in events in ascending time order (tasks with earlier // times first). nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare); for (size_t i = 0; i < eventTasks.size(); ++i) { if (!affectedTracks.contains(eventTasks[i].second->track())) affectedTracks.append(eventTasks[i].second->track()); // 13 - Queue each task in events, in list order. // Each event in eventTasks may be either an enterEvent or an exitEvent, // depending on the time that is associated with the event. This // correctly identifies the type of the event, if the startTime is // less than the endTime in the cue. if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) { mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::enter, eventTasks[i].second)); mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::exit, eventTasks[i].second)); } else { bool isEnterEvent = eventTasks[i].first == eventTasks[i].second->startTime(); AtomicString eventName = isEnterEvent ? EventTypeNames::enter : EventTypeNames::exit; mediaElement.scheduleEvent(createEventWithTarget(eventName, eventTasks[i].second)); } } // 14 - Sort affected tracks in the same order as the text tracks appear in // the media element's list of text tracks, and remove duplicates. nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare); // 15 - For each text track in affected tracks, in the list order, queue a // task to fire a simple event named cuechange at the TextTrack object, and, ... for (size_t i = 0; i < affectedTracks.size(); ++i) { mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::cuechange, affectedTracks[i])); // ... if the text track has a corresponding track element, to then fire a // simple event named cuechange at the track element as well. if (affectedTracks[i]->trackType() == TextTrack::TrackElement) { HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i].get())->trackElement(); ASSERT(trackElement); mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::cuechange, trackElement)); } } // 16 - Set the text track cue active flag of all the cues in the current // cues, and unset the text track cue active flag of all the cues in the // other cues. for (size_t i = 0; i < currentCuesSize; ++i) currentCues[i].data()->setIsActive(true); for (size_t i = 0; i < previousCuesSize; ++i) { if (!currentCues.contains(previousCues[i])) { TextTrackCue* cue = previousCues[i].data(); cue->setIsActive(false); cue->removeDisplayTree(); } } // Update the current active cues. m_currentlyActiveCues = currentCues; mediaElement.updateTextTrackDisplay(); }