PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const { Vector<RefPtr<Node> > result; execute<true>(rootNode, result); if (result.isEmpty()) return 0; ASSERT(result.size() == 1); ASSERT(result.first()->isElementNode()); return static_cast<Element*>(result.first().get()); }
void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem elements()->namedItems(name, namedItems); Node* elementFromPast = elementFromPastNamesMap(name); if (namedItems.size() && namedItems.first() != elementFromPast) addToPastNamesMap(namedItems.first().get(), name); else if (elementFromPast && namedItems.isEmpty()) namedItems.append(elementFromPast); }
void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem elements()->namedItems(name, namedItems); // FIXME: The specification says we should not add the element from the past when names map when namedItems is not empty. HTMLFormControlElement* elementFromPast = elementFromPastNamesMap(name); if (namedItems.size() == 1 && namedItems.first() != elementFromPast) addElementToPastNamesMap(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); else if (elementFromPast && namedItems.find(elementFromPast) == notFound) namedItems.append(elementFromPast); }
// FIXME: Use Ref<HTMLElement> for the function result since there are no non-HTML elements returned here. Vector<Ref<Element>> HTMLFormElement::namedElements(const AtomicString& name) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem Vector<Ref<Element>> namedItems = elements()->namedItems(name); HTMLElement* elementFromPast = elementFromPastNamesMap(name); if (namedItems.size() == 1 && namedItems.first().ptr() != elementFromPast) addToPastNamesMap(downcast<HTMLElement>(namedItems.first().get()).asFormNamedItem(), name); else if (elementFromPast && namedItems.isEmpty()) namedItems.append(*elementFromPast); return namedItems; }
LayoutType closestSnapOffset(const Vector<LayoutType>& snapOffsets, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, LayoutType scrollDestination, float velocity, unsigned& activeSnapIndex) { ASSERT(snapOffsets.size()); activeSnapIndex = 0; unsigned lowerSnapOffsetRangeIndex; unsigned upperSnapOffsetRangeIndex; indicesOfNearestSnapOffsetRanges<LayoutType>(scrollDestination, snapOffsetRanges, lowerSnapOffsetRangeIndex, upperSnapOffsetRangeIndex); if (lowerSnapOffsetRangeIndex == upperSnapOffsetRangeIndex && upperSnapOffsetRangeIndex != invalidSnapOffsetIndex) { activeSnapIndex = invalidSnapOffsetIndex; return scrollDestination; } if (scrollDestination <= snapOffsets.first()) return snapOffsets.first(); activeSnapIndex = snapOffsets.size() - 1; if (scrollDestination >= snapOffsets.last()) return snapOffsets.last(); unsigned lowerIndex; unsigned upperIndex; indicesOfNearestSnapOffsets<LayoutType>(scrollDestination, snapOffsets, lowerIndex, upperIndex); LayoutType lowerSnapPosition = snapOffsets[lowerIndex]; LayoutType upperSnapPosition = snapOffsets[upperIndex]; if (!std::abs(velocity)) { bool isCloserToLowerSnapPosition = scrollDestination - lowerSnapPosition <= upperSnapPosition - scrollDestination; activeSnapIndex = isCloserToLowerSnapPosition ? lowerIndex : upperIndex; return isCloserToLowerSnapPosition ? lowerSnapPosition : upperSnapPosition; } // Non-zero velocity indicates a flick gesture. Even if another snap point is closer, we should choose the one in the direction of the flick gesture // as long as a scroll snap offset range does not lie between the scroll destination and the targeted snap offset. if (velocity < 0) { if (lowerSnapOffsetRangeIndex != invalidSnapOffsetIndex && lowerSnapPosition < snapOffsetRanges[lowerSnapOffsetRangeIndex].end) { activeSnapIndex = upperIndex; return upperSnapPosition; } activeSnapIndex = lowerIndex; return lowerSnapPosition; } if (upperSnapOffsetRangeIndex != invalidSnapOffsetIndex && snapOffsetRanges[upperSnapOffsetRangeIndex].start < upperSnapPosition) { activeSnapIndex = lowerIndex; return lowerSnapPosition; } activeSnapIndex = upperIndex; return upperSnapPosition; }
SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bool syntheticBold, bool syntheticItalic) : m_orientation(Horizontal) , m_platformData(FontPlatformData(size, syntheticBold, syntheticItalic)) , m_treatAsFixedPitch(false) , m_svgFontData(svgFontData) , m_isCustomFont(true) , m_isLoading(false) , m_isBrokenIdeographFont(false) { SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement(); unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); float scale = size; if (unitsPerEm) scale /= unitsPerEm; float xHeight = svgFontFaceElement->xHeight() * scale; float ascent = svgFontFaceElement->ascent() * scale; float descent = svgFontFaceElement->descent() * scale; float lineGap = 0.1f * size; m_fontMetrics.setUnitsPerEm(unitsPerEm); m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); m_fontMetrics.setXHeight(xHeight); SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); Vector<SVGGlyphIdentifier> spaceGlyphs; associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs); m_spaceWidth = spaceGlyphs.isEmpty() ? xHeight : spaceGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> numeralZeroGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs); m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : numeralZeroGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> letterWGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs); m_maxCharWidth = letterWGlyphs.isEmpty() ? ascent : letterWGlyphs.first().horizontalAdvanceX * scale; // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above? m_spaceGlyph = 0; m_zeroWidthSpaceGlyph = 0; determinePitch(); m_adjustedSpaceWidth = roundf(m_spaceWidth); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) { elements()->namedItems(name, namedItems); HTMLFormControlElement* aliasElement = elementForAlias(name); if (aliasElement) { if (namedItems.find(aliasElement) == notFound) { // We have seen it before but it is gone now. Still, we need to return it. // FIXME: The above comment is not clear enough; it does not say why we need to do this. namedItems.append(aliasElement); } } if (namedItems.size() && namedItems.first() != aliasElement) addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); }
static void indicesOfNearestSnapOffsetRanges(LayoutType offset, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, unsigned& lowerIndex, unsigned& upperIndex) { if (snapOffsetRanges.isEmpty()) { lowerIndex = invalidSnapOffsetIndex; upperIndex = invalidSnapOffsetIndex; return; } int lowerIndexAsInt = -1; int upperIndexAsInt = snapOffsetRanges.size(); do { int middleIndex = (lowerIndexAsInt + upperIndexAsInt) / 2; auto& range = snapOffsetRanges[middleIndex]; if (range.start < offset && offset < range.end) { lowerIndexAsInt = middleIndex; upperIndexAsInt = middleIndex; break; } if (offset > range.end) lowerIndexAsInt = middleIndex; else upperIndexAsInt = middleIndex; } while (lowerIndexAsInt < upperIndexAsInt - 1); if (offset <= snapOffsetRanges.first().start) lowerIndex = invalidSnapOffsetIndex; else lowerIndex = lowerIndexAsInt; if (offset >= snapOffsetRanges.last().end) upperIndex = invalidSnapOffsetIndex; else upperIndex = upperIndexAsInt; }
// Fills bounds with points from argv starting at firstCoord; also resizes selected to match the size of bounds static void readPolyBounds(S32 argc, const char **argv, S32 firstCoord, F32 gridSize, bool allowFirstAndLastPointToBeEqual, Vector<Point> &bounds, Vector<bool> &selected) { Point p, lastP; bool isTwoPointLine = (argc - firstCoord) / 2 == 2; bounds.clear(); // Make sure we don't crash with firstCoord = 0; argc = 7; or some uneven number for(S32 i = firstCoord; i < argc - 1; i += 2) { // If we are loading legacy levels (earlier than 019), then we have a gridsize multiplier if(gridSize != 1.f) p.set( (F32) (atof(argv[i]) * gridSize), (F32) (atof(argv[i+1]) * gridSize ) ); else p.set( (F32) atof(argv[i]), (F32) atof(argv[i+1])); // Normally, we'll want to filter out adjacent points that are identical. But we also // need to handle the situation where the user has created a 2-pt 0-length line. // Because the users demand it. We will deliver. if(i == firstCoord || p != lastP || isTwoPointLine) bounds.push_back(p); lastP.set(p); } // Check if last point was same as first; if so, scrap it if(!allowFirstAndLastPointToBeEqual && bounds.first() == bounds.last()) bounds.erase(bounds.size() - 1); selected.resize(bounds.size()); }
PassRefPtr<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JSC::JSValue& exception, size_t maxStackSize) { Vector<ScriptCallFrame> frames; RefCountedArray<StackFrame> stackTrace = exec->vm().exceptionStack(); for (size_t i = 0; i < stackTrace.size() && i < maxStackSize; i++) { if (!stackTrace[i].callee && frames.size()) break; String functionName = stackTrace[i].friendlyFunctionName(exec); unsigned line; unsigned column; stackTrace[i].computeLineAndColumn(line, column); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL, line, column)); } // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions // Fallback to getting at least the line and sourceURL from the exception if it has values and the exceptionStack doesn't. if (frames.size() > 0) { const ScriptCallFrame& firstCallFrame = frames.first(); JSObject* exceptionObject = exception.toObject(exec); if (exception.isObject() && firstCallFrame.sourceURL().isEmpty()) { JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line")); int lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0; JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL")); String exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined"); frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, lineNumber, 0); } } return ScriptCallStack::create(frames); }
SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bool syntheticBold, bool syntheticItalic) : m_orientation(Horizontal) , m_platformData(FontPlatformData(size, syntheticBold, syntheticItalic)) , m_treatAsFixedPitch(false) , m_svgFontData(svgFontData) , m_isCustomFont(true) , m_isLoading(false) , m_isBrokenIdeographFont(false) { SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement(); m_unitsPerEm = svgFontFaceElement->unitsPerEm(); double scale = size; if (m_unitsPerEm) scale /= m_unitsPerEm; m_ascent = static_cast<int>(svgFontFaceElement->ascent() * scale); m_descent = static_cast<int>(svgFontFaceElement->descent() * scale); m_xHeight = static_cast<int>(svgFontFaceElement->xHeight() * scale); m_lineGap = 0.1f * size; m_lineSpacing = m_ascent + m_descent + m_lineGap; SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); Vector<SVGGlyphIdentifier> spaceGlyphs; associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs); m_spaceWidth = spaceGlyphs.isEmpty() ? m_xHeight : static_cast<float>(spaceGlyphs.first().horizontalAdvanceX * scale); Vector<SVGGlyphIdentifier> numeralZeroGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs); m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : static_cast<float>(numeralZeroGlyphs.first().horizontalAdvanceX * scale); Vector<SVGGlyphIdentifier> letterWGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs); m_maxCharWidth = letterWGlyphs.isEmpty() ? m_ascent : static_cast<float>(letterWGlyphs.first().horizontalAdvanceX * scale); // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above? m_spaceGlyph = 0; m_zeroWidthSpaceGlyph = 0; determinePitch(); m_adjustedSpaceWidth = roundf(m_spaceWidth); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
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(); Vector<Element*>* idResults = idCache(name); if (idResults && !idResults->isEmpty()) return idResults->first(); Vector<Element*>* nameResults = nameCache(name); if (nameResults && !nameResults->isEmpty()) return nameResults->first(); return 0; }
static void adjustAxisSnapOffsetsForScrollExtent(Vector<LayoutUnit>& snapOffsets, float maxScrollExtent) { if (snapOffsets.isEmpty()) return; std::sort(snapOffsets.begin(), snapOffsets.end()); if (snapOffsets.last() != maxScrollExtent) snapOffsets.append(maxScrollExtent); if (snapOffsets.first()) snapOffsets.insert(0, 0); }
void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) { elements()->namedItems(name, namedItems); // see if we have seen something with this name before RefPtr<HTMLFormControlElement> aliasElem; if ((aliasElem = elementForAlias(name))) { bool found = false; for (unsigned n = 0; n < namedItems.size(); n++) { if (namedItems[n] == aliasElem.get()) { found = true; break; } } if (!found) // we have seen it before but it is gone now. still, we need to return it. namedItems.append(aliasElem.get()); } // name has been accessed, remember it if (namedItems.size() && aliasElem != namedItems.first()) addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); }
void HTMLFormControlsCollection::namedGetter(const AtomicString& name, bool& radioNodeListEnabled, RefPtr<RadioNodeList>& radioNodeList, bool& elementEnabled, RefPtr<Element>& element) { Vector<RefPtr<Element> > namedItems; this->namedItems(name, namedItems); if (namedItems.isEmpty()) return; if (namedItems.size() == 1) { elementEnabled = true; element = namedItems.first(); return; } radioNodeListEnabled = true; radioNodeList = ownerNode().radioNodeList(name); }
static void updateFromStyle(Vector<LayoutUnit>& snapOffsets, const RenderStyle& style, ScrollEventAxis axis, LayoutUnit viewSize, LayoutUnit scrollSize, Vector<LayoutUnit>& snapOffsetSubsequence) { std::sort(snapOffsetSubsequence.begin(), snapOffsetSubsequence.end()); if (snapOffsetSubsequence.isEmpty()) snapOffsetSubsequence.append(0); auto* points = (axis == ScrollEventAxis::Horizontal) ? style.scrollSnapPointsX() : style.scrollSnapPointsY(); bool hasRepeat = points ? points->hasRepeat : false; LayoutUnit repeatOffset = points ? valueForLength(points->repeatOffset, viewSize) : LayoutUnit::fromPixel(1); repeatOffset = std::max<LayoutUnit>(repeatOffset, LayoutUnit::fromPixel(1)); LayoutUnit destinationOffset = destinationOffsetForViewSize(axis, style.scrollSnapDestination(), viewSize); LayoutUnit curSnapPositionShift = 0; LayoutUnit maxScrollOffset = scrollSize - viewSize; LayoutUnit lastSnapPosition = curSnapPositionShift; do { for (auto& snapPosition : snapOffsetSubsequence) { LayoutUnit potentialSnapPosition = curSnapPositionShift + snapPosition - destinationOffset; if (potentialSnapPosition < 0) continue; if (potentialSnapPosition >= maxScrollOffset) break; // Don't add another zero offset value. if (potentialSnapPosition) snapOffsets.append(potentialSnapPosition); lastSnapPosition = potentialSnapPosition + destinationOffset; } curSnapPositionShift = lastSnapPosition + repeatOffset; } while (hasRepeat && curSnapPositionShift < maxScrollOffset); if (snapOffsets.isEmpty()) return; // Always put a snap point on the zero offset. if (snapOffsets.first()) snapOffsets.insert(0, 0); // Always put a snap point on the maximum scroll offset. // Not a part of the spec, but necessary to prevent unreachable content when snapping. if (snapOffsets.last() != maxScrollOffset) snapOffsets.append(maxScrollOffset); }
static void printTransitions(const DFA& dfa, unsigned sourceNodeId) { const DFANode& sourceNode = dfa.nodes[sourceNodeId]; auto transitions = sourceNode.transitions(dfa); if (transitions.begin() == transitions.end()) return; HashMap<unsigned, Vector<uint16_t>, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> transitionsPerTarget; // First, we build the list of transitions coming to each target node. for (const auto& transition : transitions) { unsigned target = transition.target(); transitionsPerTarget.add(target, Vector<uint16_t>()); for (unsigned offset = 0; offset < transition.range().size(); ++offset) transitionsPerTarget.find(target)->value.append(transition.first() + offset); } // Then we go over each one an display the ranges one by one. for (const auto& transitionPerTarget : transitionsPerTarget) { dataLogF(" %d -> %d [label=\"", sourceNodeId, transitionPerTarget.key); Vector<uint16_t> incommingCharacters = transitionPerTarget.value; std::sort(incommingCharacters.begin(), incommingCharacters.end()); char rangeStart = incommingCharacters.first(); char rangeEnd = rangeStart; bool first = true; for (unsigned sortedTransitionIndex = 1; sortedTransitionIndex < incommingCharacters.size(); ++sortedTransitionIndex) { char nextChar = incommingCharacters[sortedTransitionIndex]; if (nextChar == rangeEnd+1) { rangeEnd = nextChar; continue; } printRange(first, rangeStart, rangeEnd); rangeStart = rangeEnd = nextChar; first = false; } printRange(first, rangeStart, rangeEnd); dataLogF("\"];\n"); } }
static void printTransitions(const Vector<NFANode>& graph, unsigned sourceNode, uint16_t epsilonTransitionCharacter) { const NFANode& node = graph[sourceNode]; const HashMap<uint16_t, NFANodeIndexSet, DefaultHash<uint16_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint16_t>>& transitions = node.transitions; HashMap<unsigned, HashSet<uint16_t, DefaultHash<uint16_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint16_t>>, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> transitionsPerTarget; for (const auto& transition : transitions) { for (unsigned targetNode : transition.value) { transitionsPerTarget.add(targetNode, HashSet<uint16_t, DefaultHash<uint16_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint16_t>>()); transitionsPerTarget.find(targetNode)->value.add(transition.key); } } for (const auto& transitionPerTarget : transitionsPerTarget) { dataLogF(" %d -> %d [label=\"", sourceNode, transitionPerTarget.key); Vector<uint16_t> incommingCharacters; copyToVector(transitionPerTarget.value, incommingCharacters); std::sort(incommingCharacters.begin(), incommingCharacters.end()); uint16_t rangeStart = incommingCharacters.first(); uint16_t rangeEnd = rangeStart; bool first = true; for (unsigned sortedTransitionIndex = 1; sortedTransitionIndex < incommingCharacters.size(); ++sortedTransitionIndex) { uint16_t nextChar = incommingCharacters[sortedTransitionIndex]; if (nextChar == rangeEnd+1) { rangeEnd = nextChar; continue; } printRange(first, rangeStart, rangeEnd, epsilonTransitionCharacter); rangeStart = rangeEnd = nextChar; first = false; } printRange(first, rangeStart, rangeEnd, epsilonTransitionCharacter); dataLogF("\"];\n"); } for (unsigned targetOnAnyCharacter : node.transitionsOnAnyCharacter) dataLogF(" %d -> %d [label=\"[any input]\"];\n", sourceNode, targetOnAnyCharacter); }
static inline PassRefPtr<StringImpl> joinStrings(const Vector<String>& strings, const String& separator, unsigned outputLength) { ASSERT(outputLength); CharacterType* data; RefPtr<StringImpl> outputStringImpl = StringImpl::tryCreateUninitialized(outputLength, data); if (!outputStringImpl) return PassRefPtr<StringImpl>(); const String firstString = strings.first(); appendStringToData(data, firstString); for (size_t i = 1; i < strings.size(); ++i) { appendStringToData(data, separator); appendStringToData(data, strings[i]); } ASSERT(data == (outputStringImpl->getCharacters<CharacterType>() + outputStringImpl->length())); return outputStringImpl.release(); }
Ref<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JSC::Exception* exception, size_t maxStackSize) { Vector<ScriptCallFrame> frames; auto& stackTrace = exception->stack(); VM& vm = exec->vm(); for (size_t i = 0; i < stackTrace.size() && i < maxStackSize; i++) { unsigned line; unsigned column; stackTrace[i].computeLineAndColumn(line, column); String functionName = stackTrace[i].functionName(vm); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL(), static_cast<SourceID>(stackTrace[i].sourceID()), line, column)); } // Fallback to getting at least the line and sourceURL from the exception object if it has values and the exceptionStack doesn't. if (exception->value().isObject()) { JSObject* exceptionObject = exception->value().toObject(exec); ASSERT(exceptionObject); int lineNumber; int columnNumber; String exceptionSourceURL; if (!frames.size()) { if (extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL)) frames.append(ScriptCallFrame(String(), exceptionSourceURL, noSourceID, lineNumber, columnNumber)); } else { // FIXME: The typical stack trace will have a native frame at the top, and consumers of // this code already know this (see JSDOMExceptionHandling.cpp's reportException, for // example - it uses firstNonNativeCallFrame). This looks like it splats something else // over it. That something else is probably already at stackTrace[1]. // https://bugs.webkit.org/show_bug.cgi?id=176663 if (!stackTrace[0].hasLineAndColumnInfo() || stackTrace[0].sourceURL().isEmpty()) { const ScriptCallFrame& firstCallFrame = frames.first(); if (extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL)) frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, stackTrace[0].sourceID(), lineNumber, columnNumber); } } } return ScriptCallStack::create(frames); }
bool SVGFontData::fillBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, const SimpleFontData* fontData) const { bool haveGlyphs = false; Vector<SVGGlyph> glyphs; for (unsigned i = 0; i < length; ++i) { String lookupString(buffer + i, 1); fontElement->collectGlyphsForString(lookupString, glyphs); if (glyphs.isEmpty()) { pageToFill->setGlyphDataForIndex(offset + i, 0, 0); continue; } // Associate entry in glyph page with first valid SVGGlyph. // If there are multiple valid ones, just take the first one. WidthIterator will take // care of matching to the correct glyph, if multiple ones are available, as that's // only possible within the context of a string (eg. arabic form matching). haveGlyphs = true; pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry, fontData); glyphs.clear(); } return haveGlyphs; }
static void dumpVariable( Stream& stream, Dictionary::Entry* entry, const char* inClass = NULL ) { // Skip variables defined in script. if( entry->type < 0 ) return; // Skip internals... don't export them. if ( entry->mUsage && ( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) ) return; // Split up qualified name. Vector< String > nameComponents; String( entry->name ).split( "::", nameComponents ); if( !nameComponents.size() ) // Safety check. return; // Match filter. if( inClass ) { // Make sure first qualifier in name components is a // namespace qualifier matching the given class name. if( nameComponents.size() <= 1 || dStricmp( nameComponents.first().c_str() + 1, inClass ) != 0 ) // Skip '$'. return; } else { // Make sure, this is *not* in a class namespace. if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep ) return; } // Skip variables for which we can't decipher their type. ConsoleBaseType* type = ConsoleBaseType::getType( entry->type ); if( !type ) { Con::errorf( "Can't find type for variable '%s'", entry->name ); return; } // Write doc comment. stream.writeText( "/*!\r\n" ); if( !inClass ) { stream.writeText( "@var " ); stream.writeText( type->getTypeClassName() ); stream.writeText( " " ); stream.writeText( entry->name ); stream.writeText( ";\r\n" ); } dumpDoc( stream, entry->mUsage ); stream.writeText( "*/\r\n" ); // Write definition. const U32 numNameComponents = nameComponents.size(); if( !inClass && numNameComponents > 1 ) for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) { stream.writeText( "namespace " ); stream.writeText( nameComponents[ i ] ); stream.writeText( " { " ); } if( inClass ) stream.writeText( "static " ); if( entry->mIsConstant ) stream.writeText( "const " ); stream.writeText( type->getTypeClassName() ); stream.writeText( " " ); stream.writeText( nameComponents.last() ); stream.writeText( ";" ); if( !inClass && numNameComponents > 1 ) for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) stream.writeText( " } " ); stream.writeText( "\r\n" ); }
bool decodeResourceError(ArgumentDecoder* decoder, WebCore::ResourceError& resourceError) { String domain; int errorCode; String failingURL; String localizedDescription; if (!decoder->decode(CoreIPC::Out(domain, errorCode, failingURL, localizedDescription))) return false; #if USE(CFNETWORK) WebKit::PlatformCertificateInfo certificate; if (!decoder->decode(certificate)) return false; const Vector<PCCERT_CONTEXT> certificateChain = certificate.certificateChain(); if (!certificateChain.isEmpty()) { ASSERT(certificateChain.size() == 1); resourceError = WebCore::ResourceError(domain, errorCode, failingURL, localizedDescription, WebCore::copyCertificateToData(certificateChain.first()).get()); return true; } #endif resourceError = WebCore::ResourceError(domain, errorCode, failingURL, localizedDescription); return true; }
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 = endingSelection().start().downstream(); // Find the top-most blockquote from the start. Node* topBlockquote = highestEnclosingNodeOfType(pos, isMailBlockquote); if (!topBlockquote || !topBlockquote->parentNode() || !topBlockquote->isElementNode()) return; RefPtr<Element> breakNode = 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(breakNode.get(), topBlockquote); setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.get()), DOWNSTREAM, endingSelection().isDirectional())); rebalanceWhitespace(); return; } // Insert a break after the top blockquote. insertNodeAfter(breakNode.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(breakNode.get()), 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)) pos = pos.next(); // Adjust the position so we don't split at the beginning of a quote. while (isFirstVisiblePositionInNode(VisiblePosition(pos), enclosingNodeOfType(pos, isMailBlockquote))) pos = pos.previous(); // startNode is the first node that we need to move to the new blockquote. Node* startNode = pos.deprecatedNode(); // Split at pos if in the middle of a text node. if (startNode->isTextNode()) { Text* textNode = toText(startNode); if ((unsigned)pos.deprecatedEditingOffset() >= textNode->length()) { startNode = NodeTraversal::next(startNode); ASSERT(startNode); } else if (pos.deprecatedEditingOffset() > 0) splitTextNode(textNode, pos.deprecatedEditingOffset()); } else if (pos.deprecatedEditingOffset() > 0) { Node* childAtOffset = startNode->childNode(pos.deprecatedEditingOffset()); 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(VisiblePosition(firstPositionInOrBeforeNode(startNode)), endingSelection().isDirectional())); return; } // Build up list of ancestors in between the start node and the top blockquote. Vector<RefPtr<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. RefPtr<Element> clonedBlockquote = toElement(topBlockquote)->cloneElementWithoutChildren(); insertNodeAfter(clonedBlockquote.get(), breakNode.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). RefPtr<Element> clonedAncestor = clonedBlockquote; for (size_t i = ancestors.size(); i != 0; --i) { RefPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren(); // Preserve list item numbering in cloned lists. if (clonedChild->isElementNode() && clonedChild->hasTagName(olTag)) { 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 && !listChildNode->hasTagName(liTag)) listChildNode = listChildNode->nextSibling(); if (listChildNode && listChildNode->renderer() && listChildNode->renderer()->isListItem()) setNodeAttribute(clonedChild, startAttr, String::number(toRenderListItem(listChildNode->renderer())->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. RefPtr<Element> ancestor; RefPtr<Element> clonedParent; 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 Node* originalParent = ancestors.first().get(); if (!originalParent->hasChildNodes()) removeNode(originalParent); } // Make sure the cloned block quote renders. addBlockPlaceholderIfNeeded(clonedBlockquote.get()); // Put the selection right before the break. setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.get()), DOWNSTREAM, endingSelection().isDirectional())); rebalanceWhitespace(); }
static Vector<SVGGlyphIdentifier::ArabicForm> charactersWithArabicForm(const String& input, bool rtl) { Vector<SVGGlyphIdentifier::ArabicForm> forms; unsigned length = input.length(); bool containsArabic = false; for (unsigned i = 0; i < length; ++i) { if (isArabicChar(input[i])) { containsArabic = true; break; } } if (!containsArabic) return forms; bool lastCharShapesRight = false; // Start identifying arabic forms if (rtl) { for (int i = length - 1; i >= 0; --i) forms.prepend(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first())); } else { for (unsigned i = 0; i < length; ++i) forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last())); } return forms; }
inline static bool ancestorsCrossShadowBoundaries(const Vector<EventContext>& ancestors) { return ancestors.isEmpty() || ancestors.first().node() == ancestors.last().node(); }
void TSLastDetail::_update() { // We're gonna render... make sure we can. bool sceneBegun = GFX->canCurrentlyRender(); if ( !sceneBegun ) GFX->beginScene(); _validateDim(); Vector<GBitmap*> bitmaps; Vector<GBitmap*> normalmaps; // We need to create our own instance to render with. TSShapeInstance *shape = new TSShapeInstance( mShape, true ); // Animate the shape once. shape->animate( mDl ); // So we don't have to change it everywhere. const GFXFormat format = GFXFormatR8G8B8A8; S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 ); // Figure out the optimal texture size. Point2I texSize( smMaxTexSize, smMaxTexSize ); while ( true ) { Point2I halfSize( texSize.x / 2, texSize.y / 2 ); U32 count = ( halfSize.x / mDim ) * ( halfSize.y / mDim ); if ( count < imposterCount ) { // Try half of the height. count = ( texSize.x / mDim ) * ( halfSize.y / mDim ); if ( count >= imposterCount ) texSize.y = halfSize.y; break; } texSize = halfSize; } GBitmap *imposter = NULL; GBitmap *normalmap = NULL; GBitmap destBmp( texSize.x, texSize.y, true, format ); GBitmap destNormal( texSize.x, texSize.y, true, format ); U32 mipLevels = destBmp.getNumMipLevels(); ImposterCapture *imposterCap = new ImposterCapture(); F32 equatorStepSize = M_2PI_F / (F32)mNumEquatorSteps; static const MatrixF topXfm( EulerF( -M_PI_F / 2.0f, 0, 0 ) ); static const MatrixF bottomXfm( EulerF( M_PI_F / 2.0f, 0, 0 ) ); MatrixF angMat; F32 polarStepSize = 0.0f; if ( mNumPolarSteps > 0 ) polarStepSize = -( 0.5f * M_PI_F - mDegToRad( mPolarAngle ) ) / (F32)mNumPolarSteps; PROFILE_START(TSLastDetail_snapshots); S32 currDim = mDim; for ( S32 mip = 0; mip < mipLevels; mip++ ) { if ( currDim < 1 ) currDim = 1; dMemset( destBmp.getWritableBits(mip), 0, destBmp.getWidth(mip) * destBmp.getHeight(mip) * GFXFormat_getByteSize( format ) ); dMemset( destNormal.getWritableBits(mip), 0, destNormal.getWidth(mip) * destNormal.getHeight(mip) * GFXFormat_getByteSize( format ) ); bitmaps.clear(); normalmaps.clear(); F32 rotX = 0.0f; if ( mNumPolarSteps > 0 ) rotX = -( mDegToRad( mPolarAngle ) - 0.5f * M_PI_F ); // We capture the images in a particular order which must // match the order expected by the imposter renderer. imposterCap->begin( shape, mDl, currDim, mRadius, mCenter ); for ( U32 j=0; j < (2 * mNumPolarSteps + 1); j++ ) { F32 rotZ = -M_PI_F / 2.0f; for ( U32 k=0; k < mNumEquatorSteps; k++ ) { angMat.mul( MatrixF( EulerF( rotX, 0, 0 ) ), MatrixF( EulerF( 0, 0, rotZ ) ) ); imposterCap->capture( angMat, &imposter, &normalmap ); bitmaps.push_back( imposter ); normalmaps.push_back( normalmap ); rotZ += equatorStepSize; } rotX += polarStepSize; if ( mIncludePoles ) { imposterCap->capture( topXfm, &imposter, &normalmap ); bitmaps.push_back(imposter); normalmaps.push_back( normalmap ); imposterCap->capture( bottomXfm, &imposter, &normalmap ); bitmaps.push_back( imposter ); normalmaps.push_back( normalmap ); } } imposterCap->end(); Point2I texSize( destBmp.getWidth(mip), destBmp.getHeight(mip) ); // Ok... pack in bitmaps till we run out. for ( S32 y=0; y+currDim <= texSize.y; ) { for ( S32 x=0; x+currDim <= texSize.x; ) { // Copy the next bitmap to the dest texture. GBitmap* bmp = bitmaps.first(); bitmaps.pop_front(); destBmp.copyRect( bmp, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip ); delete bmp; // Copy the next normal to the dest texture. GBitmap* normalmap = normalmaps.first(); normalmaps.pop_front(); destNormal.copyRect( normalmap, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip ); delete normalmap; // Did we finish? if ( bitmaps.empty() ) break; x += currDim; } // Did we finish? if ( bitmaps.empty() ) break; y += currDim; } // Next mip... currDim /= 2; } PROFILE_END(); // TSLastDetail_snapshots delete imposterCap; delete shape; // Should we dump the images? if ( Con::getBoolVariable( "$TSLastDetail::dumpImposters", false ) ) { String imposterPath = mCachePath + ".imposter.png"; String normalsPath = mCachePath + ".imposter_normals.png"; FileStream stream; if ( stream.open( imposterPath, Torque::FS::File::Write ) ) destBmp.writeBitmap( "png", stream ); stream.close(); if ( stream.open( normalsPath, Torque::FS::File::Write ) ) destNormal.writeBitmap( "png", stream ); stream.close(); } // DEBUG: Some code to force usage of a test image. //GBitmap* tempMap = GBitmap::load( "./forest/data/test1234.png" ); //tempMap->extrudeMipLevels(); //mTexture.set( tempMap, &GFXDefaultStaticDiffuseProfile, false ); //delete tempMap; DDSFile *ddsDest = DDSFile::createDDSFileFromGBitmap( &destBmp ); DDSUtil::squishDDS( ddsDest, GFXFormatDXT3 ); DDSFile *ddsNormals = DDSFile::createDDSFileFromGBitmap( &destNormal ); DDSUtil::squishDDS( ddsNormals, GFXFormatDXT5 ); // Finally save the imposters to disk. FileStream fs; if ( fs.open( _getDiffuseMapPath(), Torque::FS::File::Write ) ) { ddsDest->write( fs ); fs.close(); } if ( fs.open( _getNormalMapPath(), Torque::FS::File::Write ) ) { ddsNormals->write( fs ); fs.close(); } delete ddsDest; delete ddsNormals; // If we did a begin then end it now. if ( !sceneBegun ) GFX->endScene(); }
bool ArgumentCoder<ResourceError>::decode(ArgumentDecoder* decoder, ResourceError& resourceError) { String domain; if (!decoder->decode(domain)) return false; int errorCode; if (!decoder->decode(errorCode)) return false; String failingURL; if (!decoder->decode(failingURL)) return false; String localizedDescription; if (!decoder->decode(localizedDescription)) return false; #if USE(CFNETWORK) PlatformCertificateInfo certificate; if (!decoder->decode(certificate)) return false; const Vector<PCCERT_CONTEXT> certificateChain = certificate.certificateChain(); if (!certificateChain.isEmpty()) { ASSERT(certificateChain.size() == 1); resourceError = ResourceError(domain, errorCode, failingURL, localizedDescription, copyCertificateToData(certificateChain.first()).get()); return true; } #endif resourceError = ResourceError(domain, errorCode, failingURL, localizedDescription); return true; }
void PluginDatabase::loadPersistentMetadataCache() { if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) return; PlatformFileHandle file; String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); file = openFile(absoluteCachePath, OpenForRead); if (!isHandleValid(file)) return; // Mark cache as loaded regardless of success or failure. If // there's error in the cache, we won't try to load it anymore. m_persistentMetadataCacheIsLoaded = true; Vector<char> fileContents; fillBufferWithContentsOfFile(file, fileContents); closeFile(file); if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') { LOG_ERROR("Unable to read plugin metadata cache: corrupt schema"); deleteFile(absoluteCachePath); return; } char* bufferPos = fileContents.data() + 1; char* end = fileContents.data() + fileContents.size(); PluginSet cachedPlugins; HashMap<String, time_t> cachedPluginPathsWithTimes; HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath; while (bufferPos < end) { String path; time_t lastModified; String name; String desc; String mimeDesc; if (!(readUTF8String(path, bufferPos, end) && readTime(lastModified, bufferPos, end) && readUTF8String(name, bufferPos, end) && readUTF8String(desc, bufferPos, end) && readUTF8String(mimeDesc, bufferPos, end))) { LOG_ERROR("Unable to read plugin metadata cache: corrupt data"); deleteFile(absoluteCachePath); return; } // Skip metadata that points to plugins from directories that // are not part of plugin directory list anymore. String pluginDirectoryName = directoryName(path); if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound) continue; RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc); if (package && cachedPlugins.add(package).isNewEntry) { cachedPluginPathsWithTimes.add(package->path(), package->lastModified()); cachedPluginsByPath.add(package->path(), package); } } m_plugins.swap(cachedPlugins); m_pluginsByPath.swap(cachedPluginsByPath); m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes); }
void InsertParagraphSeparatorCommand::doApply() { bool splitText = false; if (endingSelection().isNone()) return; Position insertionPosition = endingSelection().start(); EAffinity affinity = endingSelection().affinity(); // Delete the current selection. if (endingSelection().isRange()) { calculateStyleBeforeInsertion(insertionPosition); deleteSelection(false, true); insertionPosition = endingSelection().start(); affinity = endingSelection().affinity(); } // FIXME: The rangeCompliantEquivalent conversion needs to be moved into enclosingBlock. Node* startBlockNode = enclosingBlock(rangeCompliantEquivalent(insertionPosition).node()); Position canonicalPos = VisiblePosition(insertionPosition).deepEquivalent(); Element* startBlock = static_cast<Element*>(startBlockNode); if (!startBlockNode || !startBlockNode->isElementNode() || !startBlock->parentNode() || isTableCell(startBlock) || startBlock->hasTagName(formTag) || (canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable()) || canonicalPos.node()->hasTagName(hrTag)) { applyCommandToComposite(InsertLineBreakCommand::create(document())); return; } // Use the leftmost candidate. insertionPosition = insertionPosition.upstream(); if (!insertionPosition.isCandidate()) insertionPosition = insertionPosition.downstream(); // Adjust the insertion position after the delete insertionPosition = positionAvoidingSpecialElementBoundary(insertionPosition); VisiblePosition visiblePos(insertionPosition, affinity); calculateStyleBeforeInsertion(insertionPosition); //--------------------------------------------------------------------- // Handle special case of typing return on an empty list item if (breakOutOfEmptyListItem()) return; //--------------------------------------------------------------------- // Prepare for more general cases. bool isFirstInBlock = isStartOfBlock(visiblePos); bool isLastInBlock = isEndOfBlock(visiblePos); bool nestNewBlock = false; // Create block to be inserted. RefPtr<Element> blockToInsert; if (startBlock == startBlock->rootEditableElement()) { blockToInsert = createDefaultParagraphElement(document()); nestNewBlock = true; } else if (shouldUseDefaultParagraphElement(startBlock)) blockToInsert = createDefaultParagraphElement(document()); else blockToInsert = startBlock->cloneElementWithoutChildren(); //--------------------------------------------------------------------- // Handle case when position is in the last visible position in its block, // including when the block is empty. if (isLastInBlock) { bool shouldApplyStyleAfterInsertion = true; if (nestNewBlock) { if (isFirstInBlock && !lineBreakExistsAtVisiblePosition(visiblePos)) { // The block is empty. Create an empty block to // represent the paragraph that we're leaving. RefPtr<Element> extraBlock = createDefaultParagraphElement(document()); appendNode(extraBlock, startBlock); appendBlockPlaceholder(extraBlock); } appendNode(blockToInsert, startBlock); } else { // We can get here if we pasted a copied portion of a blockquote with a newline at the end and are trying to paste it // into an unquoted area. We then don't want the newline within the blockquote or else it will also be quoted. if (Node* highestBlockquote = highestEnclosingNodeOfType(canonicalPos, &isMailBlockquote)) { startBlock = static_cast<Element*>(highestBlockquote); // When inserting the newline after the blockquote, we don't want to apply the original style after the insertion shouldApplyStyleAfterInsertion = false; } insertNodeAfter(blockToInsert, startBlock); } appendBlockPlaceholder(blockToInsert); setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM)); if (shouldApplyStyleAfterInsertion) applyStyleAfterInsertion(startBlock); return; } //--------------------------------------------------------------------- // Handle case when position is in the first visible position in its block, and // similar case where previous position is in another, presumeably nested, block. if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { Node *refNode; if (isFirstInBlock && !nestNewBlock) refNode = startBlock; else if (insertionPosition.node() == startBlock && nestNewBlock) { refNode = startBlock->childNode(insertionPosition.deprecatedEditingOffset()); ASSERT(refNode); // must be true or we'd be in the end of block case } else refNode = insertionPosition.node(); // find ending selection position easily before inserting the paragraph insertionPosition = insertionPosition.downstream(); insertNodeBefore(blockToInsert, refNode); appendBlockPlaceholder(blockToInsert.get()); setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM)); applyStyleAfterInsertion(startBlock); setEndingSelection(VisibleSelection(insertionPosition, DOWNSTREAM)); return; } //--------------------------------------------------------------------- // Handle the (more complicated) general case, // All of the content in the current block after visiblePos is // about to be wrapped in a new paragraph element. Add a br before // it if visiblePos is at the start of a paragraph so that the // content will move down a line. if (isStartOfParagraph(visiblePos)) { RefPtr<Element> br = createBreakElement(document()); insertNodeAt(br.get(), insertionPosition); insertionPosition = positionAfterNode(br.get()); } // Move downstream. Typing style code will take care of carrying along the // style of the upstream position. insertionPosition = insertionPosition.downstream(); // At this point, the insertionPosition's node could be a container, and we want to make sure we include // all of the correct nodes when building the ancestor list. So this needs to be the deepest representation of the position // before we walk the DOM tree. insertionPosition = VisiblePosition(insertionPosition).deepEquivalent(); // Build up list of ancestors in between the start node and the start block. Vector<Element*> ancestors; if (insertionPosition.node() != startBlock) { for (Element* n = insertionPosition.node()->parentElement(); n && n != startBlock; n = n->parentElement()) ancestors.append(n); } // Make sure we do not cause a rendered space to become unrendered. // FIXME: We need the affinity for pos, but pos.downstream() does not give it Position leadingWhitespace = insertionPosition.leadingWhitespacePosition(VP_DEFAULT_AFFINITY); // FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions // after the preserved newline, causing the newline to be turned into a nbsp. if (leadingWhitespace.isNotNull()) { Text* textNode = static_cast<Text*>(leadingWhitespace.node()); ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } // Split at pos if in the middle of a text node. if (insertionPosition.node()->isTextNode()) { Text* textNode = static_cast<Text*>(insertionPosition.node()); bool atEnd = (unsigned)insertionPosition.deprecatedEditingOffset() >= textNode->length(); if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) { splitTextNode(textNode, insertionPosition.deprecatedEditingOffset()); insertionPosition.moveToOffset(0); visiblePos = VisiblePosition(insertionPosition); splitText = true; } } // Put the added block in the tree. if (nestNewBlock) appendNode(blockToInsert.get(), startBlock); else insertNodeAfter(blockToInsert.get(), startBlock); updateLayout(); // Make clones of ancestors in between the start node and the start block. RefPtr<Element> parent = blockToInsert; for (size_t i = ancestors.size(); i != 0; --i) { RefPtr<Element> child = ancestors[i - 1]->cloneElementWithoutChildren(); appendNode(child, parent); parent = child.release(); } // If the paragraph separator was inserted at the end of a paragraph, an empty line must be // created. All of the nodes, starting at visiblePos, are about to be added to the new paragraph // element. If the first node to be inserted won't be one that will hold an empty line open, add a br. if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtVisiblePosition(visiblePos)) appendNode(createBreakElement(document()).get(), blockToInsert.get()); // Move the start node and the siblings of the start node. if (insertionPosition.node() != startBlock) { Node* n = insertionPosition.node(); if (insertionPosition.deprecatedEditingOffset() >= caretMaxOffset(n)) n = n->nextSibling(); while (n && n != blockToInsert) { Node *next = n->nextSibling(); removeNode(n); appendNode(n, parent.get()); n = next; } } // Move everything after the start node. if (!ancestors.isEmpty()) { Element* leftParent = ancestors.first(); while (leftParent && leftParent != startBlock) { parent = parent->parentElement(); if (!parent) break; Node* n = leftParent->nextSibling(); while (n && n != blockToInsert) { Node* next = n->nextSibling(); removeNode(n); appendNode(n, parent.get()); n = next; } leftParent = leftParent->parentElement(); } } // Handle whitespace that occurs after the split if (splitText) { updateLayout(); insertionPosition = Position(insertionPosition.node(), 0); if (!insertionPosition.isRenderedCharacter()) { // Clear out all whitespace and insert one non-breaking space ASSERT(insertionPosition.node()->isTextNode()); ASSERT(!insertionPosition.node()->renderer() || insertionPosition.node()->renderer()->style()->collapseWhiteSpace()); deleteInsignificantTextDownstream(insertionPosition); insertTextIntoNode(static_cast<Text*>(insertionPosition.node()), 0, nonBreakingSpaceString()); } } setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM)); applyStyleAfterInsertion(startBlock); }