IDBTransaction* IDBDatabase::transaction( ScriptState* scriptState, const StringOrStringSequenceOrDOMStringList& storeNames, const String& modeString, ExceptionState& exceptionState) { IDB_TRACE("IDBDatabase::transaction"); recordApiCallsHistogram(IDBTransactionCall); HashSet<String> scope; if (storeNames.isString()) { scope.add(storeNames.getAsString()); } else if (storeNames.isStringSequence()) { for (const String& name : storeNames.getAsStringSequence()) scope.add(name); } else if (storeNames.isDOMStringList()) { const Vector<String>& list = *storeNames.getAsDOMStringList(); for (const String& name : list) scope.add(name); } else { NOTREACHED(); } if (m_versionChangeTransaction) { exceptionState.throwDOMException( InvalidStateError, "A version change transaction is running."); return nullptr; } if (m_closePending) { exceptionState.throwDOMException(InvalidStateError, "The database connection is closing."); return nullptr; } if (!m_backend) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage); return nullptr; } if (scope.isEmpty()) { exceptionState.throwDOMException(InvalidAccessError, "The storeNames parameter was empty."); return nullptr; } Vector<int64_t> objectStoreIds; for (const String& name : scope) { int64_t objectStoreId = findObjectStoreId(name); if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { exceptionState.throwDOMException( NotFoundError, "One of the specified object stores was not found."); return nullptr; } objectStoreIds.append(objectStoreId); } WebIDBTransactionMode mode = IDBTransaction::stringToMode(modeString); if (mode != WebIDBTransactionModeReadOnly && mode != WebIDBTransactionModeReadWrite) { exceptionState.throwTypeError( "The mode provided ('" + modeString + "') is not one of 'readonly' or 'readwrite'."); return nullptr; } int64_t transactionId = nextTransactionId(); m_backend->createTransaction(transactionId, objectStoreIds, mode); return IDBTransaction::createNonVersionChange(scriptState, transactionId, scope, mode, this); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "URL has fragment component " + m_url.elidedString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked"); m_state = CLOSED; ec = SECURITY_ERR; return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (scriptExecutionContext()->isDocument()) { Document* document = toDocument(scriptExecutionContext()); shouldBypassMainWorldContentSecurityPolicy = document->frame()->script()->shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_channel = WebSocketChannel::create(scriptExecutionContext(), this); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (visited.contains(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } visited.add(protocols[i]); } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
static void addEncodingName(HashSet<const char*>& set, const char* name) { const char* atomicName = atomicCanonicalTextEncodingName(name); if (atomicName) set.add(atomicName); }
PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, Vector<Node*>& nodes) { ASSERT(frame); const ResourceResponse& response = frame->loader()->documentLoader()->response(); KURL responseURL = response.url(); // it's possible to have a response without a URL here // <rdar://problem/5454935> if (responseURL.isNull()) responseURL = KURL(""); PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->name()); Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; Vector<PassRefPtr<ArchiveResource> > subresources; HashSet<KURL> uniqueSubresources; Vector<Node*>::iterator it = nodes.begin(); Vector<Node*>::iterator end = nodes.end(); for (; it != end; ++it) { Frame* childFrame; if (((*it)->hasTagName(HTMLNames::frameTag) || (*it)->hasTagName(HTMLNames::iframeTag) || (*it)->hasTagName(HTMLNames::objectTag)) && (childFrame = static_cast<HTMLFrameOwnerElement*>(*it)->contentFrame())) { RefPtr<LegacyWebArchive> subframeArchive; if (Document* document = childFrame->document()) subframeArchive = LegacyWebArchive::create(document); else subframeArchive = create(childFrame); if (subframeArchive) subframeArchives.append(subframeArchive); else LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data()); } else { ListHashSet<KURL> subresourceURLs; (*it)->getSubresourceURLs(subresourceURLs); DocumentLoader* documentLoader = frame->loader()->documentLoader(); ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end(); for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) { const KURL& subresourceURL = *iter; if (uniqueSubresources.contains(subresourceURL)) continue; uniqueSubresources.add(subresourceURL); RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL); if (resource) { subresources.append(resource.release()); continue; } CachedResource *cachedResource = cache()->resourceForURL(subresourceURL); if (cachedResource) { resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response()); if (resource) { subresources.append(resource.release()); continue; } } // FIXME: should do something better than spew to console here LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data()); } } } return create(mainResource, subresources, subframeArchives); }
void SVGFitToViewBox::addSupportedAttributes(HashSet<QualifiedName>& supportedAttributes) { supportedAttributes.add(SVGNames::viewBoxAttr); supportedAttributes.add(SVGNames::preserveAspectRatioAttr); }
void validate() { // NB. This code is not written for performance, since it is not intended to run // in release builds. // Validate that all local variables at the head of the root block are dead. BasicBlock* root = m_graph.m_blocks[0].get(); for (unsigned i = 0; i < root->variablesAtHead.numberOfLocals(); ++i) V_EQUAL((static_cast<VirtualRegister>(i), 0), static_cast<Node*>(0), root->variablesAtHead.local(i)); // Validate ref counts and uses. HashMap<Node*, unsigned> myRefCounts; for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block || !block->isReachable) continue; for (size_t i = 0; i < block->numNodes(); ++i) myRefCounts.add(block->node(i), 0); } HashSet<Node*> acceptableNodes; for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block || !block->isReachable) continue; for (size_t i = 0; i < block->numNodes(); ++i) { Node* node = block->node(i); acceptableNodes.add(node); if (!node->shouldGenerate()) continue; for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { // Phi children in LoadStore form are invalid. if (m_graph.m_form == LoadStore && block->isPhiIndex(i)) continue; Edge edge = m_graph.child(node, j); if (!edge) continue; myRefCounts.find(edge.node())->value++; // Unless I'm a Flush, Phantom, GetLocal, or Phi, my children should hasResult(). switch (node->op()) { case Flush: case GetLocal: case PhantomLocal: VALIDATE((node, edge), edge->hasVariableAccessData()); VALIDATE((node, edge), edge->variableAccessData() == node->variableAccessData()); break; case Phi: VALIDATE((node, edge), edge->hasVariableAccessData()); if (m_graph.m_unificationState == LocallyUnified) break; VALIDATE((node, edge), edge->variableAccessData() == node->variableAccessData()); break; case Phantom: if (m_graph.m_form == LoadStore && !j) break; default: VALIDATE((node, edge), edge->hasResult()); break; } } } } for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block || !block->isReachable) continue; HashSet<Node*> phisInThisBlock; HashSet<Node*> nodesInThisBlock; for (size_t i = 0; i < block->numNodes(); ++i) { Node* node = block->node(i); nodesInThisBlock.add(node); if (block->isPhiIndex(i)) phisInThisBlock.add(node); if (m_graph.m_form == ThreadedCPS || !node->hasVariableAccessData()) V_EQUAL((node), myRefCounts.get(node), node->adjustedRefCount()); else VALIDATE((node), myRefCounts.get(node) ? node->adjustedRefCount() : true); for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { Edge edge = m_graph.child(node, j); if (!edge) continue; VALIDATE((node, edge), acceptableNodes.contains(edge.node())); } } for (size_t i = 0; i < block->phis.size(); ++i) { Node* node = block->phis[i]; ASSERT(phisInThisBlock.contains(node)); VALIDATE((node), node->op() == Phi); VirtualRegister local = node->local(); for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { // Phi children in LoadStore form are invalid. if (m_graph.m_form == LoadStore && block->isPhiIndex(i)) continue; Edge edge = m_graph.child(node, j); if (!edge) continue; VALIDATE( (node, edge), edge->op() == SetLocal || edge->op() == SetArgument || edge->op() == Flush || edge->op() == Phi); if (phisInThisBlock.contains(edge.node())) continue; if (nodesInThisBlock.contains(edge.node())) { VALIDATE( (node, edge), edge->op() == SetLocal || edge->op() == SetArgument || edge->op() == Flush); continue; } // There must exist a predecessor block that has this node index in // its tail variables. bool found = false; for (unsigned k = 0; k < block->m_predecessors.size(); ++k) { BasicBlock* prevBlock = m_graph.m_blocks[block->m_predecessors[k]].get(); VALIDATE((Block, block->m_predecessors[k]), prevBlock); VALIDATE((Block, block->m_predecessors[k]), prevBlock->isReachable); Node* prevNode = prevBlock->variablesAtTail.operand(local); // If we have a Phi that is not referring to *this* block then all predecessors // must have that local available. VALIDATE((local, blockIndex, Block, block->m_predecessors[k]), prevNode); switch (prevNode->op()) { case GetLocal: case Flush: case PhantomLocal: prevNode = prevNode->child1().node(); break; default: break; } if (node->shouldGenerate()) { VALIDATE((local, block->m_predecessors[k], prevNode), prevNode->shouldGenerate()); } VALIDATE( (local, block->m_predecessors[k], prevNode), prevNode->op() == SetLocal || prevNode->op() == SetArgument || prevNode->op() == Phi); if (prevNode == edge.node()) { found = true; break; } // At this point it cannot refer into this block. VALIDATE((local, block->m_predecessors[k], prevNode), !prevBlock->isInBlock(edge.node())); } VALIDATE((node, edge), found); } } Operands<size_t> getLocalPositions( block->variablesAtHead.numberOfArguments(), block->variablesAtHead.numberOfLocals()); Operands<size_t> setLocalPositions( block->variablesAtHead.numberOfArguments(), block->variablesAtHead.numberOfLocals()); for (size_t i = 0; i < block->variablesAtHead.numberOfArguments(); ++i) { VALIDATE((static_cast<VirtualRegister>(argumentToOperand(i)), blockIndex), !block->variablesAtHead.argument(i) || block->variablesAtHead.argument(i)->hasVariableAccessData()); if (m_graph.m_form == ThreadedCPS) VALIDATE((static_cast<VirtualRegister>(argumentToOperand(i)), blockIndex), !block->variablesAtTail.argument(i) || block->variablesAtTail.argument(i)->hasVariableAccessData()); getLocalPositions.argument(i) = notSet; setLocalPositions.argument(i) = notSet; } for (size_t i = 0; i < block->variablesAtHead.numberOfLocals(); ++i) { VALIDATE((static_cast<VirtualRegister>(i), blockIndex), !block->variablesAtHead.local(i) || block->variablesAtHead.local(i)->hasVariableAccessData()); if (m_graph.m_form == ThreadedCPS) VALIDATE((static_cast<VirtualRegister>(i), blockIndex), !block->variablesAtTail.local(i) || block->variablesAtTail.local(i)->hasVariableAccessData()); getLocalPositions.local(i) = notSet; setLocalPositions.local(i) = notSet; } for (size_t i = 0; i < block->size(); ++i) { Node* node = block->at(i); ASSERT(nodesInThisBlock.contains(node)); VALIDATE((node), node->op() != Phi); for (unsigned j = 0; j < m_graph.numChildren(node); ++j) { Edge edge = m_graph.child(node, j); if (!edge) continue; VALIDATE((node, edge), nodesInThisBlock.contains(edge.node())); switch (node->op()) { case PhantomLocal: case GetLocal: case Flush: break; case Phantom: if (m_graph.m_form == LoadStore && !j) break; default: VALIDATE((node, edge), !phisInThisBlock.contains(edge.node())); break; } } if (!node->shouldGenerate()) continue; switch (node->op()) { case GetLocal: if (node->variableAccessData()->isCaptured()) break; if (m_graph.m_form == ThreadedCPS) VALIDATE((node, blockIndex), getLocalPositions.operand(node->local()) == notSet); getLocalPositions.operand(node->local()) = i; break; case SetLocal: if (node->variableAccessData()->isCaptured()) break; // Only record the first SetLocal. There may be multiple SetLocals // because of flushing. if (setLocalPositions.operand(node->local()) != notSet) break; setLocalPositions.operand(node->local()) = i; break; default: break; } } if (m_graph.m_form == LoadStore) continue; for (size_t i = 0; i < block->variablesAtHead.numberOfArguments(); ++i) { checkOperand( blockIndex, getLocalPositions, setLocalPositions, argumentToOperand(i)); } for (size_t i = 0; i < block->variablesAtHead.numberOfLocals(); ++i) { checkOperand( blockIndex, getLocalPositions, setLocalPositions, i); } } }
static void sortBlock(unsigned from, unsigned to, Vector<Vector<Node*> >& parentMatrix, bool mayContainAttributeNodes) { ASSERT(from + 1 < to); // Should not call this function with less that two nodes to sort. unsigned minDepth = UINT_MAX; for (unsigned i = from; i < to; ++i) { unsigned depth = parentMatrix[i].size() - 1; if (minDepth > depth) minDepth = depth; } // Find the common ancestor. unsigned commonAncestorDepth = minDepth; Node* commonAncestor; while (true) { commonAncestor = parentWithDepth(commonAncestorDepth, parentMatrix[from]); if (commonAncestorDepth == 0) break; bool allEqual = true; for (unsigned i = from + 1; i < to; ++i) { if (commonAncestor != parentWithDepth(commonAncestorDepth, parentMatrix[i])) { allEqual = false; break; } } if (allEqual) break; --commonAncestorDepth; } if (commonAncestorDepth == minDepth) { // One of the nodes is the common ancestor => it is the first in document order. // Find it and move it to the beginning. for (unsigned i = from; i < to; ++i) if (commonAncestor == parentMatrix[i][0]) { parentMatrix[i].swap(parentMatrix[from]); if (from + 2 < to) sortBlock(from + 1, to, parentMatrix, mayContainAttributeNodes); return; } } if (mayContainAttributeNodes && commonAncestor->isElementNode()) { // The attribute nodes and namespace nodes of an element occur before the children of the element. // The namespace nodes are defined to occur before the attribute nodes. // The relative order of namespace nodes is implementation-dependent. // The relative order of attribute nodes is implementation-dependent. unsigned sortedEnd = from; // FIXME: namespace nodes are not implemented. for (unsigned i = sortedEnd; i < to; ++i) { Node* n = parentMatrix[i][0]; if (n->isAttributeNode() && static_cast<Attr*>(n)->ownerElement() == commonAncestor) parentMatrix[i].swap(parentMatrix[sortedEnd++]); } if (sortedEnd != from) { if (to - sortedEnd > 1) sortBlock(sortedEnd, to, parentMatrix, mayContainAttributeNodes); return; } } // Children nodes of the common ancestor induce a subdivision of our node-set. // Sort it according to this subdivision, and recursively sort each group. HashSet<Node*> parentNodes; for (unsigned i = from; i < to; ++i) parentNodes.add(parentWithDepth(commonAncestorDepth + 1, parentMatrix[i])); unsigned previousGroupEnd = from; unsigned groupEnd = from; for (Node* n = commonAncestor->firstChild(); n; n = n->nextSibling()) { // If parentNodes contains the node, perform a linear search to move its children in the node-set to the beginning. if (parentNodes.contains(n)) { for (unsigned i = groupEnd; i < to; ++i) if (parentWithDepth(commonAncestorDepth + 1, parentMatrix[i]) == n) parentMatrix[i].swap(parentMatrix[groupEnd++]); if (groupEnd - previousGroupEnd > 1) sortBlock(previousGroupEnd, groupEnd, parentMatrix, mayContainAttributeNodes); ASSERT(previousGroupEnd != groupEnd); previousGroupEnd = groupEnd; #ifndef NDEBUG parentNodes.remove(n); #endif } } ASSERT(parentNodes.isEmpty()); }
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = true; } if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { if (element->hasRelativeLengths()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); } needsLayout = true; } } } SubtreeLayoutScope layoutScope(child); // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope. // Since they only care about viewport size changes (to resolve their relative lengths), we trigger // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher // SubtreeLayoutScope (in RenderView::layout()). if (needsLayout && !child->isSVGResourceContainer()) layoutScope.setNeedsLayout(child); layoutResourcesIfNeeded(child); if (child->needsLayout()) { child->layout(); // Renderers are responsible for repainting themselves when changing, except // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. // We could handle this in the individual objects, but for now it's easier to have // parent containers call repaint(). (RenderBlock::layout* has similar logic.) if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) child->repaint(); } else if (layoutSizeChanged) notlayoutedObjects.add(child); } if (!layoutSizeChanged) { ASSERT(notlayoutedObjects.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) invalidateResourcesOfChildren(*it); }
void RenderPartObject::updateWidget() { String url; String serviceType; Vector<String> paramNames; Vector<String> paramValues; Frame* frame = m_view->frame(); setNeedsLayoutAndMinMaxRecalc(); if (element()->hasTagName(objectTag)) { HTMLObjectElement* o = static_cast<HTMLObjectElement*>(element()); if (!o->isComplete()) return; // Check for a child EMBED tag. HTMLEmbedElement* embed = 0; for (Node* child = o->firstChild(); child;) { if (child->hasTagName(embedTag)) { embed = static_cast<HTMLEmbedElement*>(child); break; } else if (child->hasTagName(objectTag)) child = child->nextSibling(); // Don't descend into nested OBJECT tags else child = child->traverseNextNode(o); // Otherwise descend (EMBEDs may be inside COMMENT tags) } // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. HTMLElement *embedOrObject; if (embed) { embedOrObject = (HTMLElement *)embed; String attribute = embedOrObject->getAttribute(widthAttr); if (!attribute.isEmpty()) o->setAttribute(widthAttr, attribute); attribute = embedOrObject->getAttribute(heightAttr); if (!attribute.isEmpty()) o->setAttribute(heightAttr, attribute); url = embed->url; serviceType = embed->serviceType; } else embedOrObject = (HTMLElement *)o; // If there was no URL or type defined in EMBED, try the OBJECT tag. if (url.isEmpty()) url = o->url; if (serviceType.isEmpty()) serviceType = o->serviceType; HashSet<StringImpl*, CaseInsensitiveHash> uniqueParamNames; // Scan the PARAM children. // Get the URL and type from the params if we don't already have them. // Get the attributes from the params if there is no EMBED tag. Node *child = o->firstChild(); while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { if (child->hasTagName(paramTag)) { HTMLParamElement* p = static_cast<HTMLParamElement*>(child); String name = p->name().lower(); if (url.isEmpty() && (name == "src" || name == "movie" || name == "code" || name == "url")) url = p->value(); if (serviceType.isEmpty() && name == "type") { serviceType = p->value(); int pos = serviceType.find(";"); if (pos != -1) serviceType = serviceType.left(pos); } if (!embed && !name.isEmpty()) { uniqueParamNames.add(p->name().impl()); paramNames.append(p->name()); paramValues.append(p->value()); } } child = child->nextSibling(); } // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, // else our Java plugin will misinterpret it. [4004531] String codebase; if (!embed && serviceType.lower() == "application/x-java-applet") { codebase = "codebase"; uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already } // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. NamedAttrMap* attributes = embedOrObject->attributes(); if (attributes) { for (unsigned i = 0; i < attributes->length(); ++i) { Attribute* it = attributes->attributeItem(i); const AtomicString& name = it->name().localName(); if (embed || !uniqueParamNames.contains(name.impl())) { paramNames.append(name.domString()); paramValues.append(it->value().domString()); } } } // If we still don't have a type, try to map from a specific CLASSID to a type. if (serviceType.isEmpty() && !o->classId.isEmpty()) mapClassIdToServiceType(o->classId, serviceType); // If no URL and type, abort. if (url.isEmpty() && serviceType.isEmpty()) return; if (!isURLAllowed(document(), url)) return; // Find out if we support fallback content. m_hasFallbackContent = false; for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param> (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) m_hasFallbackContent = true; } bool success = frame->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues); if (!success && m_hasFallbackContent) o->renderFallbackContent(); } else if (element()->hasTagName(embedTag)) { HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element()); url = o->url; serviceType = o->serviceType; if (url.isEmpty() && serviceType.isEmpty()) return; if (!isURLAllowed(document(), url)) return; // add all attributes set on the embed object NamedAttrMap* a = o->attributes(); if (a) { for (unsigned i = 0; i < a->length(); ++i) { Attribute* it = a->attributeItem(i); paramNames.append(it->name().localName().domString()); paramValues.append(it->value().domString()); } } frame->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues); } }
void HTMLFormCollection::updateNameCache() const { if (info()->hasNameCache) return; HashSet<AtomicStringImpl*> foundInputElements; HTMLFormElement* f = static_cast<HTMLFormElement*>(base()); for (unsigned i = 0; i < f->formElements.size(); ++i) { HTMLFormControlElement* e = f->formElements[i]; if (e->isEnumeratable()) { const AtomicString& idAttrVal = e->getAttribute(idAttr); const AtomicString& nameAttrVal = e->getAttribute(nameAttr); if (!idAttrVal.isEmpty()) { // add to id cache Vector<Element*>* idVector = info()->idCache.get(idAttrVal.impl()); if (!idVector) { idVector = new Vector<Element*>; info()->idCache.add(idAttrVal.impl(), idVector); } idVector->append(e); foundInputElements.add(idAttrVal.impl()); } if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) { // add to name cache Vector<Element*>* nameVector = info()->nameCache.get(nameAttrVal.impl()); if (!nameVector) { nameVector = new Vector<Element*>; info()->nameCache.add(nameAttrVal.impl(), nameVector); } nameVector->append(e); foundInputElements.add(nameAttrVal.impl()); } } } for (unsigned i = 0; i < f->imgElements.size(); ++i) { HTMLImageElement* e = f->imgElements[i]; const AtomicString& idAttrVal = e->getAttribute(idAttr); const AtomicString& nameAttrVal = e->getAttribute(nameAttr); if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl())) { // add to id cache Vector<Element*>* idVector = info()->idCache.get(idAttrVal.impl()); if (!idVector) { idVector = new Vector<Element*>; info()->idCache.add(idAttrVal.impl(), idVector); } idVector->append(e); } if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl())) { // add to name cache Vector<Element*>* nameVector = info()->nameCache.get(nameAttrVal.impl()); if (!nameVector) { nameVector = new Vector<Element*>; info()->nameCache.add(nameAttrVal.impl(), nameVector); } nameVector->append(e); } } info()->hasNameCache = true; }
static HashSet<String> mimeTypeCache() { do_gst_init(); static HashSet<String> cache; static bool typeListInitialized = false; if (!typeListInitialized) { // These subtypes are already beeing supported by WebKit itself HashSet<String> ignoredApplicationSubtypes; ignoredApplicationSubtypes.add(String("javascript")); ignoredApplicationSubtypes.add(String("ecmascript")); ignoredApplicationSubtypes.add(String("x-javascript")); ignoredApplicationSubtypes.add(String("xml")); ignoredApplicationSubtypes.add(String("xhtml+xml")); ignoredApplicationSubtypes.add(String("rss+xml")); ignoredApplicationSubtypes.add(String("atom+xml")); ignoredApplicationSubtypes.add(String("x-ftp-directory")); ignoredApplicationSubtypes.add(String("x-java-applet")); ignoredApplicationSubtypes.add(String("x-java-bean")); ignoredApplicationSubtypes.add(String("x-java-vm")); ignoredApplicationSubtypes.add(String("x-shockwave-flash")); GList* factories = gst_type_find_factory_get_list(); for (GList* iterator = factories; iterator; iterator = iterator->next) { GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data); GstCaps* caps = gst_type_find_factory_get_caps(factory); // Splitting the capability by comma and taking the first part // as capability can be something like "audio/x-wavpack, framed=(boolean)false" GOwnPtr<gchar> capabilityString(gst_caps_to_string(caps)); gchar** capability = g_strsplit(capabilityString.get(), ",", 2); gchar** mimetype = g_strsplit(capability[0], "/", 2); // GStreamer plugins can be capable of supporting types which WebKit supports // by default. In that case, we should not consider these types supportable by GStreamer. // Examples of what GStreamer can support but should not be added: // text/plain, text/html, image/jpeg, application/xml if (g_str_equal(mimetype[0], "audio") || g_str_equal(mimetype[0], "video") || (g_str_equal(mimetype[0], "application") && !ignoredApplicationSubtypes.contains(String(mimetype[1])))) { cache.add(String(capability[0])); // These formats are supported by GStreamer, but not correctly advertised if (g_str_equal(capability[0], "video/x-h264") || g_str_equal(capability[0], "audio/x-m4a")) { cache.add(String("video/mp4")); cache.add(String("audio/aac")); } if (g_str_equal(capability[0], "video/x-theora")) cache.add(String("video/ogg")); if (g_str_equal(capability[0], "audio/x-wav")) cache.add(String("audio/wav")); if (g_str_equal(capability[0], "audio/mpeg")) { // This is what we are handling: mpegversion=(int)1, layer=(int)[ 1, 3 ] gchar** versionAndLayer = g_strsplit(capability[1], ",", 2); if (g_str_has_suffix (versionAndLayer[0], "(int)1")) { for (int i = 0; versionAndLayer[1][i] != '\0'; i++) { if (versionAndLayer[1][i] == '1') cache.add(String("audio/mp1")); else if (versionAndLayer[1][i] == '2') cache.add(String("audio/mp2")); else if (versionAndLayer[1][i] == '3') cache.add(String("audio/mp3")); } } g_strfreev(versionAndLayer); } } g_strfreev(capability); g_strfreev(mimetype); } gst_plugin_feature_list_free(factories); typeListInitialized = true; } return cache; }
void SVGExternalResourcesRequired::addSupportedAttributes(HashSet<QualifiedName>& supportedAttributes) { supportedAttributes.add(SVGNames::externalResourcesRequiredAttr); }
PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID) { SQLiteStatement cacheStatement(m_database, "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id " "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?"); if (cacheStatement.prepare() != SQLResultOk) { LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg()); return 0; } cacheStatement.bindInt64(1, storageID); RefPtr<ApplicationCache> cache = ApplicationCache::create(); int result; while ((result = cacheStatement.step()) == SQLResultRow) { KURL url(cacheStatement.getColumnText(0)); unsigned type = (unsigned)cacheStatement.getColumnInt64(1); Vector<char> blob; cacheStatement.getColumnBlobAsVector(5, blob); RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob); String mimeType = cacheStatement.getColumnText(2); String textEncodingName = cacheStatement.getColumnText(3); ResourceResponse response(url, mimeType, data->size(), textEncodingName, ""); String headers = cacheStatement.getColumnText(4); parseHeaders(headers, response); RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release()); if (type & ApplicationCacheResource::Manifest) cache->setManifestResource(resource.release()); else cache->addResource(resource.release()); } if (result != SQLResultDone) LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg()); // Load the online whitelist SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?"); if (whitelistStatement.prepare() != SQLResultOk) return 0; whitelistStatement.bindInt64(1, storageID); HashSet<String> whitelist; while ((result = whitelistStatement.step()) == SQLResultRow) whitelist.add(whitelistStatement.getColumnText(0)); if (result != SQLResultDone) LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg()); cache->setOnlineWhitelist(whitelist); cache->setStorageID(storageID); return cache.release(); }
RadialGradientAttributes SVGRadialGradientElement::collectGradientProperties() const { RadialGradientAttributes attributes; HashSet<const SVGGradientElement*> processedGradients; bool isRadial = true; const SVGGradientElement* current = this; while (current) { if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr)) attributes.setSpreadMethod((SVGGradientSpreadMethod) current->spreadMethod()); if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr)) attributes.setBoundingBoxMode(current->getAttribute(SVGNames::gradientUnitsAttr) == "objectBoundingBox"); if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix()); if (!attributes.hasStops()) { const Vector<SVGGradientStop>& stops(current->buildStops()); if (!stops.isEmpty()) attributes.setStops(stops); } if (isRadial) { const SVGRadialGradientElement* radial = static_cast<const SVGRadialGradientElement*>(current); if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr)) attributes.setCx(radial->cx().valueAsPercentage()); if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr)) attributes.setCy(radial->cy().valueAsPercentage()); if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr)) attributes.setR(radial->r().valueAsPercentage()); if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr)) attributes.setFx(radial->fx().valueAsPercentage()); if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr)) attributes.setFy(radial->fy().valueAsPercentage()); } processedGradients.add(current); // Respect xlink:href, take attributes from referenced element Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href())); if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) { current = static_cast<const SVGGradientElement*>(const_cast<const Node*>(refNode)); // Cycle detection if (processedGradients.contains(current)) return RadialGradientAttributes(); isRadial = current->gradientType() == RadialGradientPaintServer; } else current = 0; } // Handle default values for fx/fy if (!attributes.hasFx()) attributes.setFx(attributes.cx()); if (!attributes.hasFy()) attributes.setFy(attributes.cy()); return attributes; }
void TableSectionPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutRect localPaintInvalidationRect = paintInfo.rect; localPaintInvalidationRect.moveBy(-paintOffset); LayoutRect tableAlignedRect = m_renderTableSection.logicalRectForWritingModeAndDirection(localPaintInvalidationRect); CellSpan dirtiedRows = m_renderTableSection.dirtiedRows(tableAlignedRect); CellSpan dirtiedColumns = m_renderTableSection.dirtiedColumns(tableAlignedRect); WillBeHeapHashSet<RawPtrWillBeMember<RenderTableCell> > overflowingCells = m_renderTableSection.overflowingCells(); if (dirtiedColumns.start() < dirtiedColumns.end()) { if (!m_renderTableSection.hasMultipleCellLevels() && !overflowingCells.size()) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { // Collapsed borders are painted from the bottom right to the top left so that precedence // due to cell position is respected. for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) { unsigned row = r - 1; for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) { unsigned col = c - 1; RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(row, col); RenderTableCell* cell = current.primaryCell(); if (!cell || (row > dirtiedRows.start() && m_renderTableSection.primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(row, col - 1) == cell)) continue; LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cell, paintOffset); TableCellPainter(*cell).paintCollapsedBorders(paintInfo, cellPoint); } } } else { // Draw the dirty cells in the order that they appear. for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { RenderTableRow* row = m_renderTableSection.rowRendererAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c); RenderTableCell* cell = current.primaryCell(); if (!cell || (r > dirtiedRows.start() && m_renderTableSection.primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(r, c - 1) == cell)) continue; paintCell(cell, paintInfo, paintOffset); } } } } else { // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet. #if ENABLE(ASSERT) unsigned totalRows = m_renderTableSection.numRows(); unsigned totalCols = m_renderTableSection.table()->columns().size(); ASSERT(overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath); #endif // To make sure we properly paint invalidate the section, we paint invalidated all the overflowing cells that we collected. Vector<RenderTableCell*> cells; copyToVector(overflowingCells, cells); HashSet<RenderTableCell*> spanningCells; for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { RenderTableRow* row = m_renderTableSection.rowRendererAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c); if (!current.hasCells()) continue; for (unsigned i = 0; i < current.cells.size(); ++i) { if (overflowingCells.contains(current.cells[i])) continue; if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) { if (!spanningCells.add(current.cells[i]).isNewEntry) continue; } cells.append(current.cells[i]); } } } // Sort the dirty cells by paint order. if (!overflowingCells.size()) std::stable_sort(cells.begin(), cells.end(), compareCellPositions); else std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells); if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { for (unsigned i = cells.size(); i > 0; --i) { LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cells[i - 1], paintOffset); TableCellPainter(*cells[i - 1]).paintCollapsedBorders(paintInfo, cellPoint); } } else { for (unsigned i = 0; i < cells.size(); ++i) paintCell(cells[i], paintInfo, paintOffset); } } } }
static void addString(HashSet<String, CaseFoldingHash>& set, const char* string) { set.add(string); }
PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, const Vector<Node*>& nodes, FrameFilter* frameFilter) { ASSERT(frame); const ResourceResponse& response = frame->loader().documentLoader()->response(); URL responseURL = response.url(); // it's possible to have a response without a URL here // <rdar://problem/5454935> if (responseURL.isNull()) responseURL = URL(ParsedURLString, emptyString()); RefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree().uniqueName()); Vector<PassRefPtr<LegacyWebArchive>> subframeArchives; Vector<PassRefPtr<ArchiveResource>> subresources; HashSet<URL> uniqueSubresources; size_t nodesSize = nodes.size(); for (size_t i = 0; i < nodesSize; ++i) { Node& node = *nodes[i]; Frame* childFrame; if ((isHTMLFrameElement(node) || isHTMLIFrameElement(node) || isHTMLObjectElement(node)) && (childFrame = toHTMLFrameOwnerElement(node).contentFrame())) { if (frameFilter && !frameFilter->shouldIncludeSubframe(childFrame)) continue; RefPtr<LegacyWebArchive> subframeArchive = create(childFrame->document(), frameFilter); if (subframeArchive) subframeArchives.append(subframeArchive); else LOG_ERROR("Unabled to archive subframe %s", childFrame->tree().uniqueName().string().utf8().data()); } else { ListHashSet<URL> subresourceURLs; node.getSubresourceURLs(subresourceURLs); DocumentLoader* documentLoader = frame->loader().documentLoader(); ListHashSet<URL>::iterator iterEnd = subresourceURLs.end(); for (ListHashSet<URL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) { const URL& subresourceURL = *iter; if (uniqueSubresources.contains(subresourceURL)) continue; uniqueSubresources.add(subresourceURL); RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL); if (resource) { subresources.append(resource.release()); continue; } ResourceRequest request(subresourceURL); #if ENABLE(CACHE_PARTITIONING) request.setCachePartition(frame->document()->topOrigin()->cachePartition()); #endif CachedResource* cachedResource = memoryCache()->resourceForRequest(request); if (cachedResource) { ResourceBuffer* data = cachedResource->resourceBuffer(); resource = ArchiveResource::create(data ? data->sharedBuffer() : 0, subresourceURL, cachedResource->response()); if (resource) { subresources.append(resource.release()); continue; } } // FIXME: should do something better than spew to console here LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data()); } } } // Add favicon if one exists for this page, if we are archiving the entire page. if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase().isEnabled()) { const String& iconURL = iconDatabase().synchronousIconURLForPageURL(responseURL); if (!iconURL.isEmpty() && iconDatabase().synchronousIconDataKnownForIconURL(iconURL)) { if (Image* iconImage = iconDatabase().synchronousIconForPageURL(responseURL, IntSize(16, 16))) { if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), URL(ParsedURLString, iconURL), "image/x-icon", "", "")) subresources.append(resource.release()); } } } return create(mainResource.release(), subresources, subframeArchives); }
// FIXME: This function should not deal with url or serviceType! void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) { HashSet<StringImpl*, ASCIICaseInsensitiveHash> uniqueParamNames; String urlParameter; // Scan the PARAM children and store their name/value pairs. // Get the URL and type from the params if we don't already have them. for (auto& param : childrenOfType<HTMLParamElement>(*this)) { String name = param.name(); if (name.isEmpty()) continue; uniqueParamNames.add(name.impl()); paramNames.append(param.name()); paramValues.append(param.value()); // FIXME: url adjustment does not belong in this function. if (url.isEmpty() && urlParameter.isEmpty() && (equalLettersIgnoringASCIICase(name, "src") || equalLettersIgnoringASCIICase(name, "movie") || equalLettersIgnoringASCIICase(name, "code") || equalLettersIgnoringASCIICase(name, "url"))) urlParameter = stripLeadingAndTrailingHTMLSpaces(param.value()); // FIXME: serviceType calculation does not belong in this function. if (serviceType.isEmpty() && equalLettersIgnoringASCIICase(name, "type")) { serviceType = param.value(); size_t pos = serviceType.find(';'); if (pos != notFound) serviceType = serviceType.left(pos); } } // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, // else our Java plugin will misinterpret it. [4004531] String codebase; if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { codebase = "codebase"; uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already } // Turn the attributes of the <object> element into arrays, but don't override <param> values. if (hasAttributes()) { for (const Attribute& attribute : attributesIterator()) { const AtomicString& name = attribute.name().localName(); if (!uniqueParamNames.contains(name.impl())) { paramNames.append(name.string()); paramValues.append(attribute.value().string()); } } } mapDataParamToSrc(paramNames, paramValues); // HTML5 says that an object resource's URL is specified by the object's data // attribute, not by a param element. However, for compatibility, allow the // resource's URL to be given by a param named "src", "movie", "code" or "url" // if we know that resource points to a plug-in. #if PLATFORM(IOS) if (shouldNotPerformURLAdjustment()) return; #endif if (url.isEmpty() && !urlParameter.isEmpty()) { SubframeLoader& loader = document().frame()->loader().subframeLoader(); if (loader.resourceWillUsePlugin(urlParameter, serviceType)) url = urlParameter; } }
void SVGURIReference::addSupportedAttributes(HashSet<QualifiedName>& supportedAttributes) { supportedAttributes.add(XLinkNames::hrefAttr); }
void AccessibilityTable::addChildren() { if (!isDataTable()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer) return; RenderTable* table = static_cast<RenderTable*>(m_renderer); AXObjectCache* axCache = m_renderer->document()->axObjectCache(); // go through all the available sections to pull out the rows // and add them as children RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); if (!tableSection) return; RenderTableSection* initialTableSection = tableSection; while (tableSection) { HashSet<AccessibilityObject*> appendedRows; unsigned numRows = tableSection->numRows(); unsigned numCols = tableSection->numColumns(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { for (unsigned colIndex = 0; colIndex < numCols; ++colIndex) { RenderTableCell* cell = tableSection->cellAt(rowIndex, colIndex).cell; if (!cell) continue; AccessibilityObject* rowObject = axCache->get(cell->parent()); if (!rowObject->isTableRow()) continue; AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(rowObject); // we need to check every cell for a new row, because cell spans // can cause us to mess rows if we just check the first column if (appendedRows.contains(row)) continue; row->setRowIndex((int)m_rows.size()); m_rows.append(row); m_children.append(row); appendedRows.add(row); } } tableSection = table->sectionBelow(tableSection, true); } // make the columns based on the number of columns in the first body unsigned length = initialTableSection->numColumns(); for (unsigned i = 0; i < length; ++i) { AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->get(ColumnRole)); column->setColumnIndex((int)i); column->setParentTable(this); m_columns.append(column); m_children.append(column); } AccessibilityObject* headerContainerObject = headerContainer(); if (headerContainerObject) m_children.append(headerContainerObject); }
void AXTable::addChildren() { if (!isAXTable()) { AXRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer || !m_renderer->isTable()) return; RenderTable* table = toRenderTable(m_renderer); AXObjectCache* axCache = m_renderer->document().axObjectCache(); // Go through all the available sections to pull out the rows and add them as children. table->recalcSectionsIfNeeded(); RenderTableSection* tableSection = table->topSection(); if (!tableSection) return; RenderTableSection* initialTableSection = tableSection; while (tableSection) { HashSet<AXObject*> appendedRows; unsigned numRows = tableSection->numRows(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex); if (!renderRow) continue; AXObject* rowObject = axCache->getOrCreate(renderRow); if (!rowObject->isTableRow()) continue; AXTableRow* row = toAXTableRow(rowObject); // We need to check every cell for a new row, because cell spans // can cause us to miss rows if we just check the first column. if (appendedRows.contains(row)) continue; row->setRowIndex(static_cast<int>(m_rows.size())); m_rows.append(row); if (!row->accessibilityIsIgnored()) m_children.append(row); appendedRows.add(row); } tableSection = table->sectionBelow(tableSection, SkipEmptySections); } // make the columns based on the number of columns in the first body unsigned length = initialTableSection->numColumns(); for (unsigned i = 0; i < length; ++i) { AXTableColumn* column = toAXTableColumn(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AXObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) { WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid."); return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); return; } if (MixedContentChecker::isMixedContent(executionContext()->securityOrigin(), m_url)) { // FIXME: Throw an exception and close the connection. String message = "Connecting to a non-secure WebSocket server from a secure origin is deprecated."; executionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, message); } if (m_url.hasFragmentIdentifier()) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs."); return; } if (!portAllowed(m_url)) { m_state = CLOSED; exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed."); return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (executionContext()->isDocument()) { Document* document = toDocument(executionContext()); shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { m_state = CLOSED; // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection. exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy."); return; } m_channel = WebSocketChannel::create(executionContext(), this); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SyntaxError if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is invalid."); releaseChannel(); return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (!visited.add(protocols[i]).isNewEntry) { m_state = CLOSED; exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is duplicated."); releaseChannel(); return; } } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); }
// FIXME: This function should not deal with url or serviceType! void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) { HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; String urlParameter; // Scan the PARAM children and store their name/value pairs. // Get the URL and type from the params if we don't already have them. for (HTMLParamElement* p = Traversal<HTMLParamElement>::firstChild(*this); p; p = Traversal<HTMLParamElement>::nextSibling(*p)) { String name = p->name(); if (name.isEmpty()) continue; uniqueParamNames.add(name.impl()); paramNames.append(p->name()); paramValues.append(p->value()); // FIXME: url adjustment does not belong in this function. if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value()); // FIXME: serviceType calculation does not belong in this function. if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { serviceType = p->value(); size_t pos = serviceType.find(";"); if (pos != kNotFound) serviceType = serviceType.left(pos); } } // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, // else our Java plugin will misinterpret it. [4004531] String codebase; if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { codebase = "codebase"; uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already } // Turn the attributes of the <object> element into arrays, but don't override <param> values. AttributeCollection attributes = this->attributes(); for (const Attribute& attribute : attributes) { const AtomicString& name = attribute.name().localName(); if (!uniqueParamNames.contains(name.impl())) { paramNames.append(name.string()); paramValues.append(attribute.value().string()); } } mapDataParamToSrc(¶mNames, ¶mValues); // HTML5 says that an object resource's URL is specified by the object's data // attribute, not by a param element. However, for compatibility, allow the // resource's URL to be given by a param named "src", "movie", "code" or "url" // if we know that resource points to a plugin. if (url.isEmpty() && !urlParameter.isEmpty()) { KURL completedURL = document().completeURL(urlParameter); bool useFallback; if (shouldUsePlugin(completedURL, serviceType, false, useFallback)) url = urlParameter; } }
void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) { LOG(Network, "WebSocket %p connect to %s", this, url.utf8().data()); m_url = KURL(KURL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.string(), scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.string(), scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + m_url.string(), scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked", scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SECURITY_ERR; return; } if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectFromSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this); m_useHixie76Protocol = m_channel->useHixie76Protocol(); String protocolString; if (m_useHixie76Protocol) { if (!protocols.isEmpty()) { // Emulate JavaScript's Array.toString() behavior. protocolString = joinStrings(protocols, ","); } if (!isValidProtocolStringHixie76(protocolString)) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocolString) + "'", scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } } else { // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616], // and MUST all be unique strings." // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. for (size_t i = 0; i < protocols.size(); ++i) { if (!isValidProtocolString(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (size_t i = 0; i < protocols.size(); ++i) { if (visited.contains(protocols[i])) { scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'", scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } visited.add(protocols[i]); } if (!protocols.isEmpty()) protocolString = joinStrings(protocols, ", "); } m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
bool DOMPatchSupport::innerPatchChildren(ContainerNode* parentNode, const Vector<OwnPtr<Digest> >& oldList, const Vector<OwnPtr<Digest> >& newList, ExceptionState& exceptionState) { pair<ResultMap, ResultMap> resultMaps = diff(oldList, newList); ResultMap& oldMap = resultMaps.first; ResultMap& newMap = resultMaps.second; Digest* oldHead = 0; Digest* oldBody = 0; // 1. First strip everything except for the nodes that retain. Collect pending merges. HashMap<Digest*, Digest*> merges; HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> > usedNewOrdinals; for (size_t i = 0; i < oldList.size(); ++i) { if (oldMap[i].first) { if (usedNewOrdinals.add(oldMap[i].second).isNewEntry) continue; oldMap[i].first = 0; oldMap[i].second = 0; } // Always match <head> and <body> tags with each other - we can't remove them from the DOM // upon patching. if (isHTMLHeadElement(*oldList[i]->m_node)) { oldHead = oldList[i].get(); continue; } if (isHTMLBodyElement(*oldList[i]->m_node)) { oldBody = oldList[i].get(); continue; } // Check if this change is between stable nodes. If it is, consider it as "modified". if (!m_unusedNodesMap.contains(oldList[i]->m_sha1) && (!i || oldMap[i - 1].first) && (i == oldMap.size() - 1 || oldMap[i + 1].first)) { size_t anchorCandidate = i ? oldMap[i - 1].second + 1 : 0; size_t anchorAfter = (i == oldMap.size() - 1) ? anchorCandidate + 1 : oldMap[i + 1].second; if (anchorAfter - anchorCandidate == 1 && anchorCandidate < newList.size()) merges.set(newList[anchorCandidate].get(), oldList[i].get()); else { if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState)) return false; } } else { if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState)) return false; } } // Mark retained nodes as used, do not reuse node more than once. HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> > usedOldOrdinals; for (size_t i = 0; i < newList.size(); ++i) { if (!newMap[i].first) continue; size_t oldOrdinal = newMap[i].second; if (usedOldOrdinals.contains(oldOrdinal)) { // Do not map node more than once newMap[i].first = 0; newMap[i].second = 0; continue; } usedOldOrdinals.add(oldOrdinal); markNodeAsUsed(newMap[i].first); } // Mark <head> and <body> nodes for merge. if (oldHead || oldBody) { for (size_t i = 0; i < newList.size(); ++i) { if (oldHead && isHTMLHeadElement(*newList[i]->m_node)) merges.set(newList[i].get(), oldHead); if (oldBody && isHTMLBodyElement(*newList[i]->m_node)) merges.set(newList[i].get(), oldBody); } } // 2. Patch nodes marked for merge. for (HashMap<Digest*, Digest*>::iterator it = merges.begin(); it != merges.end(); ++it) { if (!innerPatchNode(it->value, it->key, exceptionState)) return false; } // 3. Insert missing nodes. for (size_t i = 0; i < newMap.size(); ++i) { if (newMap[i].first || merges.contains(newList[i].get())) continue; if (!insertBeforeAndMarkAsUsed(parentNode, newList[i].get(), parentNode->traverseToChildAt(i), exceptionState)) return false; } // 4. Then put all nodes that retained into their slots (sort by new index). for (size_t i = 0; i < oldMap.size(); ++i) { if (!oldMap[i].first) continue; RefPtrWillBeRawPtr<Node> node = oldMap[i].first->m_node; Node* anchorNode = parentNode->traverseToChildAt(oldMap[i].second); if (node == anchorNode) continue; if (isHTMLBodyElement(*node) || isHTMLHeadElement(*node)) continue; // Never move head or body, move the rest of the nodes around them. if (!m_domEditor->insertBefore(parentNode, node.release(), anchorNode, exceptionState)) return false; } return true; }
void SharedCookieJarQt::getHostnamesWithCookies(HashSet<String>& hostnames) { QList<QNetworkCookie> cookies = allCookies(); foreach (const QNetworkCookie& networkCookie, cookies) hostnames.add(networkCookie.domain()); }
void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(&start); bool hasSVGShadow = rendererHasSVGShadow(start); bool needsBoundariesUpdate = start.needsBoundariesUpdate(); HashSet<RenderElement*> elementsThatDidNotReceiveLayout; for (RenderObject* child = start.firstChild(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); if (needsBoundariesUpdate && hasSVGShadow) { // If we have a shadow, our shadow is baked into our children's cached boundaries, // so they need to update. child->setNeedsBoundariesUpdate(); needsLayout = true; } if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = true; } if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { if (element->hasRelativeLengths()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); } needsLayout = true; } } } if (needsLayout) child->setNeedsLayout(MarkOnlyThis); if (child->needsLayout()) { toRenderElement(child)->layout(); // Renderers are responsible for repainting themselves when changing, except // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. // We could handle this in the individual objects, but for now it's easier to have // parent containers call repaint(). (RenderBlock::layout* has similar logic.) if (!childEverHadLayout) child->repaint(); } else if (layoutSizeChanged && child->isRenderElement()) elementsThatDidNotReceiveLayout.add(toRenderElement(child)); ASSERT(!child->needsLayout()); } if (!layoutSizeChanged) { ASSERT(elementsThatDidNotReceiveLayout.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. for (auto* element : elementsThatDidNotReceiveLayout) invalidateResourcesOfChildren(*element); }
// FIXME: This function should not deal with url or serviceType! void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) { HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; String urlParameter; // Scan the PARAM children and store their name/value pairs. // Get the URL and type from the params if we don't already have them. for (Node* child = firstChild(); child; child = child->nextSibling()) { if (!child->hasTagName(paramTag)) continue; HTMLParamElement* p = static_cast<HTMLParamElement*>(child); String name = p->name(); if (name.isEmpty()) continue; uniqueParamNames.add(name.impl()); paramNames.append(p->name()); paramValues.append(p->value()); // FIXME: url adjustment does not belong in this function. if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value()); // FIXME: serviceType calculation does not belong in this function. if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { serviceType = p->value(); size_t pos = serviceType.find(";"); if (pos != notFound) serviceType = serviceType.left(pos); } } // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, // else our Java plugin will misinterpret it. [4004531] String codebase; if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { codebase = "codebase"; uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already } // Turn the attributes of the <object> element into arrays, but don't override <param> values. NamedNodeMap* attributes = this->attributes(true); if (attributes) { for (unsigned i = 0; i < attributes->length(); ++i) { Attribute* it = attributes->attributeItem(i); const AtomicString& name = it->name().localName(); if (!uniqueParamNames.contains(name.impl())) { paramNames.append(name.string()); paramValues.append(it->value().string()); } } } mapDataParamToSrc(¶mNames, ¶mValues); // HTML5 says that an object resource's URL is specified by the object's data // attribute, not by a param element. However, for compatibility, allow the // resource's URL to be given by a param named "src", "movie", "code" or "url" // if we know that resource points to a plug-in. if (url.isEmpty() && !urlParameter.isEmpty()) { SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); if (loader->resourceWillUsePlugin(urlParameter, serviceType, shouldPreferPlugInsForImages())) url = urlParameter; } }
void copyTo(HashSet<const void*>& set) { for (size_t i = 0; i < m_index; i++) set.add(m_pointers[i]); }
void MutationObserver::observe(Node* node, const MutationObserverInit& observerInit, ExceptionState& exceptionState) { DCHECK(node); MutationObserverOptions options = 0; if (observerInit.hasAttributeOldValue() && observerInit.attributeOldValue()) options |= AttributeOldValue; HashSet<AtomicString> attributeFilter; if (observerInit.hasAttributeFilter()) { const Vector<String>& sequence = observerInit.attributeFilter(); for (unsigned i = 0; i < sequence.size(); ++i) attributeFilter.add(AtomicString(sequence[i])); options |= AttributeFilter; } bool attributes = observerInit.hasAttributes() && observerInit.attributes(); if (attributes || (!observerInit.hasAttributes() && (observerInit.hasAttributeOldValue() || observerInit.hasAttributeFilter()))) options |= Attributes; if (observerInit.hasCharacterDataOldValue() && observerInit.characterDataOldValue()) options |= CharacterDataOldValue; bool characterData = observerInit.hasCharacterData() && observerInit.characterData(); if (characterData || (!observerInit.hasCharacterData() && observerInit.hasCharacterDataOldValue())) options |= CharacterData; if (observerInit.childList()) options |= ChildList; if (observerInit.subtree()) options |= Subtree; if (!(options & Attributes)) { if (options & AttributeOldValue) { exceptionState.throwTypeError( "The options object may only set 'attributeOldValue' to true when " "'attributes' is true or not present."); return; } if (options & AttributeFilter) { exceptionState.throwTypeError( "The options object may only set 'attributeFilter' when 'attributes' " "is true or not present."); return; } } if (!((options & CharacterData) || !(options & CharacterDataOldValue))) { exceptionState.throwTypeError( "The options object may only set 'characterDataOldValue' to true when " "'characterData' is true or not present."); return; } if (!(options & (Attributes | CharacterData | ChildList))) { exceptionState.throwTypeError( "The options object must set at least one of 'attributes', " "'characterData', or 'childList' to true."); return; } node->registerMutationObserver(*this, options, attributeFilter); }