static float curveLength(PathTraversalState& traversalState, CurveType curve) { static const unsigned short curveSplitDepthLimit = 20; static const double pathSegmentLengthToleranceSquared = 1.e-16; double curveScaleForToleranceSquared = curve.magnitudeSquared(); if (curveScaleForToleranceSquared < pathSegmentLengthToleranceSquared) return 0; Vector<CurveType> curveStack; curveStack.append(curve); float totalLength = 0; do { float length = curve.approximateDistance(); double lengthDiscrepancy = length - distanceLine(curve.start, curve.end); if ((lengthDiscrepancy * lengthDiscrepancy) / curveScaleForToleranceSquared > pathSegmentLengthToleranceSquared && curve.splitDepth < curveSplitDepthLimit) { CurveType leftCurve; CurveType rightCurve; curve.split(leftCurve, rightCurve); curve = leftCurve; curveStack.append(rightCurve); } else { totalLength += length; if (traversalState.m_action == PathTraversalState::TraversalPointAtLength || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) { traversalState.m_previous = curve.start; traversalState.m_current = curve.end; if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength) return totalLength; } curve = curveStack.last(); curveStack.removeLast(); } } while (!curveStack.isEmpty()); return totalLength; }
String PluginDatabase::MIMETypeForExtension(const String& extension) const { if (extension.isEmpty()) return String(); PluginSet::const_iterator end = m_plugins.end(); String mimeType; Vector<PluginPackage*, 2> pluginChoices; HashMap<PluginPackage*, String> mimeTypeForPlugin; for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end(); for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) { const Vector<String>& extensions = mime_it->second; bool foundMapping = false; for (unsigned i = 0; i < extensions.size(); i++) { if (equalIgnoringCase(extensions[i], extension)) { PluginPackage* plugin = (*it).get(); pluginChoices.append(plugin); mimeTypeForPlugin.add(plugin, mime_it->first); foundMapping = true; break; } } if (foundMapping) break; } } if (pluginChoices.isEmpty()) return String(); qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); return mimeTypeForPlugin.get(pluginChoices[0]); }
void JITWorklist::completeAllForVM(VM& vm) { DeferGC deferGC(vm.heap); for (;;) { Vector<RefPtr<Plan>, 32> myPlans; { LockHolder locker(m_lock); for (;;) { bool didFindUnfinishedPlan = false; m_plans.removeAllMatching( [&] (RefPtr<Plan>& plan) { if (plan->vm() != &vm) return false; if (!plan->isFinishedCompiling()) { didFindUnfinishedPlan = true; return false; } myPlans.append(WTFMove(plan)); return true; }); // If we found plans then we should finalize them now. if (!myPlans.isEmpty()) break; // If we don't find plans, then we're either done or we need to wait, depending on // whether we found some unfinished plans. if (!didFindUnfinishedPlan) return; m_condition.wait(m_lock); } } finalizePlans(myPlans); } }
bool SVGTextLayoutEngine::currentVisualCharacterMetrics(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& visualMetricsValues, SVGTextMetrics& visualMetrics) { ASSERT(!visualMetricsValues.isEmpty()); unsigned textMetricsSize = visualMetricsValues.size(); unsigned boxStart = textBox->start(); unsigned boxLength = textBox->len(); while (m_visualMetricsListOffset < textMetricsSize) { // Advance to text box start location. if (m_visualCharacterOffset < boxStart) { advanceToNextVisualCharacter(visualMetricsValues[m_visualMetricsListOffset]); continue; } // Stop if we've finished processing this text box. if (m_visualCharacterOffset >= boxStart + boxLength) return false; visualMetrics = visualMetricsValues[m_visualMetricsListOffset]; return true; } return false; }
void ImageEventSender::dispatchPendingEvents() { // Need to avoid re-entering this function; if new dispatches are // scheduled before the parent finishes processing the list, they // will set a timer and eventually be processed. if (!m_dispatchingList.isEmpty()) return; m_timer.stop(); m_dispatchSoonList.checkConsistency(); m_dispatchingList.swap(m_dispatchSoonList); size_t size = m_dispatchingList.size(); for (size_t i = 0; i < size; ++i) { if (ImageLoader* loader = m_dispatchingList[i]) { if (m_eventType == eventNames().beforeloadEvent) loader->dispatchPendingBeforeLoadEvent(); else loader->dispatchPendingLoadEvent(); } } m_dispatchingList.clear(); }
String DOMFilePath::removeExtraParentReferences(const String& path) { ASSERT(DOMFilePath::isAbsolute(path)); Vector<String> components; Vector<String> canonicalized; path.split(DOMFilePath::separator, components); for (size_t i = 0; i < components.size(); ++i) { if (components[i] == ".") continue; if (components[i] == "..") { if (canonicalized.size() > 0) canonicalized.pop_back(); continue; } canonicalized.append(components[i]); } if (canonicalized.isEmpty()) return DOMFilePath::root; StringBuilder result; for (size_t i = 0; i < canonicalized.size(); ++i) { result.append(DOMFilePath::separator); result.append(canonicalized[i]); } return result.toString(); }
void CSSSelectorList::adoptSelectorVector(Vector<OwnPtr<CSSParserSelector>>& selectorVector) { ASSERT_WITH_SECURITY_IMPLICATION(!selectorVector.isEmpty()); deleteSelectors(); size_t flattenedSize = 0; for (size_t i = 0; i < selectorVector.size(); ++i) { for (CSSParserSelector* selector = selectorVector[i].get(); selector; selector = selector->tagHistory()) ++flattenedSize; } ASSERT(flattenedSize); m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * flattenedSize)); size_t arrayIndex = 0; for (size_t i = 0; i < selectorVector.size(); ++i) { CSSParserSelector* current = selectorVector[i].get(); while (current) { { // Move item from the parser selector vector into m_selectorArray without invoking destructor (Ugh.) CSSSelector* currentSelector = current->releaseSelector().leakPtr(); memcpy(&m_selectorArray[arrayIndex], currentSelector, sizeof(CSSSelector)); // Free the underlying memory without invoking the destructor. operator delete (currentSelector); } current = current->tagHistory(); ASSERT(!m_selectorArray[arrayIndex].isLastInSelectorList()); if (current) m_selectorArray[arrayIndex].setNotLastInTagHistory(); ++arrayIndex; } ASSERT(m_selectorArray[arrayIndex - 1].isLastInTagHistory()); } ASSERT(flattenedSize == arrayIndex); m_selectorArray[arrayIndex - 1].setLastInSelectorList(); selectorVector.clear(); }
void computePreciseJumpTargets(CodeBlock* codeBlock, Vector<unsigned, 32>& out) { ASSERT(out.isEmpty()); // We will derive a superset of the jump targets that the code block thinks it has. // So, if the code block claims there are none, then we are done. if (!codeBlock->numberOfJumpTargets()) return; for (unsigned i = codeBlock->numberOfExceptionHandlers(); i--;) out.append(codeBlock->exceptionHandler(i).target); Interpreter* interpreter = codeBlock->vm()->interpreter; Instruction* instructionsBegin = codeBlock->instructions().begin(); unsigned instructionCount = codeBlock->instructions().size(); for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount;) { OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode); getJumpTargetsForBytecodeOffset(codeBlock, interpreter, instructionsBegin, bytecodeOffset, out); bytecodeOffset += opcodeLengths[opcodeID]; } std::sort(out.begin(), out.end()); // We will have duplicates, and we must remove them. unsigned toIndex = 0; unsigned fromIndex = 0; unsigned lastValue = UINT_MAX; while (fromIndex < out.size()) { unsigned value = out[fromIndex++]; if (value == lastValue) continue; out[toIndex++] = value; lastValue = value; } out.resize(toIndex); }
/* Computes the shortest path between the start and end locations, displaying * it on the screen and returning its length. */ static double runShortestPath(Grid<double>& world, WorldType worldType, Loc start, Loc end) { AlgorithmType algType = getAlgorithmType(); Vector<Loc> path; /* Determine which cost/heuristic functions to use. */ double (*costFn)(Loc, Loc, Grid<double>&); double (*hFn)(Loc, Loc, Grid<double>&); if (worldType == TERRAIN_WORLD) { costFn = terrainCost; hFn = terrainHeuristic; } else if (worldType == MAZE_WORLD) { costFn = mazeCost; hFn = mazeHeuristic; } else error("Unknown world type."); /* Invoke the student's shortestPath function to find out the cost of the path. * This uses the indirect calling function invoke, which is described later * on. Note that if we're using A* search, we disable the heuristic. */ path = invoke(shortestPath, start, end, world, costFn, algType == A_STAR ? hFn : zeroHeuristic); if (path.isEmpty()) { cout << "Warning: Returned path is empty." << endl; } else if (path[0] != start) { cout << "Warning: Start of path is not the start location." << endl; } else if (path[path.size() - 1] != end) { cout << "Warning: End of path is not the end location." << endl; } drawPath(path); return costOf(path, world, costFn); }
PassRefPtr<ShareableElementData> DocumentSharedObjectPool::cachedShareableElementDataWithAttributes(const Vector<Attribute>& attributes) { ASSERT(!attributes.isEmpty()); ShareableElementDataCacheKey cacheKey(attributes.data(), attributes.size()); unsigned cacheHash = cacheKey.hash(); ShareableElementDataCache::iterator cacheIterator = m_shareableElementDataCache.add(cacheHash, nullptr).iterator; if (cacheIterator->value && cacheIterator->value->key != cacheKey) cacheHash = 0; RefPtr<ShareableElementData> elementData; if (cacheHash && cacheIterator->value) elementData = cacheIterator->value->value; else elementData = ShareableElementData::createWithAttributes(attributes); if (!cacheHash || cacheIterator->value) return elementData.release(); cacheIterator->value = adoptPtr(new ShareableElementDataCacheEntry(ShareableElementDataCacheKey(elementData->immutableAttributeArray(), elementData->length()), elementData)); return elementData.release(); }
bool SizesCalcParser::handleOperator(Vector<CSSParserToken>& stack, const CSSParserToken& token) { // If the token is an operator, o1, then: // while there is an operator token, o2, at the top of the stack, and // either o1 is left-associative and its precedence is equal to that of o2, // or o1 has precedence less than that of o2, // pop o2 off the stack, onto the output queue; // push o1 onto the stack. bool stackOperatorPriority; bool incomingOperatorPriority; if (!operatorPriority(token.delimiter(), incomingOperatorPriority)) return false; if (!stack.isEmpty() && stack.last().type() == DelimiterToken) { if (!operatorPriority(stack.last().delimiter(), stackOperatorPriority)) return false; if (!incomingOperatorPriority || stackOperatorPriority) { appendOperator(stack.last()); stack.removeLast(); } } stack.append(token); return true; }
void PageRuntimeAgent::reportExecutionContextCreation() { Vector<std::pair<ScriptState*, SecurityOrigin*>> isolatedContexts; for (LocalFrame* frame : *m_inspectedFrames) { if (!frame->script().canExecuteScripts(NotAboutToExecuteScript)) continue; String frameId = IdentifiersFactory::frameId(frame); // Ensure execution context is created. // If initializeMainWorld returns true, then is registered by didCreateScriptContext if (!frame->script().initializeMainWorld()) { ScriptState* scriptState = ScriptState::forMainWorld(frame); reportExecutionContext(scriptState, true, "", frameId); } frame->script().collectIsolatedContexts(isolatedContexts); if (isolatedContexts.isEmpty()) continue; for (const auto& pair : isolatedContexts) { String originString = pair.second ? pair.second->toRawString() : ""; reportExecutionContext(pair.first, false, originString, frameId); } isolatedContexts.clear(); } }
void RenderSVGText::rebuildLayoutAttributes(Vector<SVGTextLayoutAttributes*>& affectedAttributes) { // Detect changes in layout attributes and only measure those text parts that have changed! Vector<SVGTextLayoutAttributes*> newLayoutAttributes; recursiveCollectLayoutAttributes(this, newLayoutAttributes); if (newLayoutAttributes.isEmpty()) { m_layoutAttributes.clear(); return; } // Compare m_layoutAttributes with newLayoutAttributes to figure out which attributes got added/removed. size_t size = newLayoutAttributes.size(); for (size_t i = 0; i < size; ++i) { SVGTextLayoutAttributes* attributes = newLayoutAttributes[i]; if (m_layoutAttributes.find(attributes) == notFound) m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(attributes->context()); } size = affectedAttributes.size(); for (size_t i = 0; i < size; ++i) m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(affectedAttributes[i]->context()); m_layoutAttributes = newLayoutAttributes; }
AnimatedPropertyType SVGAnimateElement::determineAnimatedPropertyType(SVGElement* targetElement) const { ASSERT(targetElement); Vector<AnimatedPropertyType> propertyTypes; targetElement->animatedPropertyTypeForAttribute(attributeName(), propertyTypes); if (propertyTypes.isEmpty()) return AnimatedUnknown; AnimatedPropertyType type = propertyTypes[0]; if (hasTagName(SVGNames::animateColorTag) && type != AnimatedColor) return AnimatedUnknown; // FIXME: Animator for AnimatedEnumeration missing. Falling back to String animation. if (type == AnimatedEnumeration) return AnimatedString; // Animations of transform lists are not allowed for <animate> or <set> // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties if (type == AnimatedTransformList) return AnimatedUnknown; return type; }
bool SVGFontData::fillNonBMPGlyphs(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) { // Each character here consists of a surrogate pair String lookupString(buffer + i * 2, 2); 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; }
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()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + m_url.string(), 0, scriptExecutionContext()->securityOrigin()->toString(), 0); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); 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()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocolString) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); 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()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); 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()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'", 0, scriptExecutionContext()->securityOrigin()->toString(), 0); 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); }
void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncoding& encoding, bool isMultiPartForm, Document* document, EncodingType encodingType) { if (isMultiPartForm) m_boundary = FormDataBuilder::generateUniqueBoundaryString(); Vector<char> encodedData; const Vector<FormDataList::Item>& items = list.items(); size_t formDataListSize = items.size(); ASSERT(!(formDataListSize % 2)); for (size_t i = 0; i < formDataListSize; i += 2) { const FormDataList::Item& key = items[i]; const FormDataList::Item& value = items[i + 1]; if (isMultiPartForm) { Vector<char> header; FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), key.data()); bool shouldGenerateFile = false; // If the current type is blob, then we also need to include the filename if (value.blob()) { String name; if (value.blob()->isFile()) { File* file = toFile(value.blob()); name = file->name(); // Let the application specify a filename if it's going to generate a replacement file for the upload. const String& path = file->path(); if (!path.isEmpty()) { if (Page* page = document->page()) { String generatedFileName; shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName); if (shouldGenerateFile) name = generatedFileName; } } // If a filename is passed in FormData.append(), use it instead of the file blob's name. if (!value.filename().isNull()) name = value.filename(); } else { // For non-file blob, use the filename if it is passed in FormData.append(). if (!value.filename().isNull()) name = value.filename(); else name = "blob"; } // We have to include the filename=".." part in the header, even if the filename is empty FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name); // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867). String contentType = value.blob()->type(); if (contentType.isEmpty()) contentType = "application/octet-stream"; ASSERT(Blob::isNormalizedContentType(contentType)); FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.ascii()); } FormDataBuilder::finishMultiPartHeader(header); // Append body appendData(header.data(), header.size()); if (value.blob()) { if (value.blob()->isFile()) { File* file = toFile(value.blob()); // Do not add the file if the path is empty. if (!file->path().isEmpty()) appendFile(file->path(), shouldGenerateFile); } else appendBlob(value.blob()->url()); } else appendData(value.data().data(), value.data().length()); appendData("\r\n", 2); } else { // Omit the name "isindex" if it's the first form data element. // FIXME: Why is this a good rule? Is this obsolete now? if (encodedData.isEmpty() && key.data() == "isindex") FormDataBuilder::encodeStringAsFormData(encodedData, value.data()); else FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data(), encodingType); } } if (isMultiPartForm) FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true); appendData(encodedData.data(), encodedData.size()); }
// This will return when (1) a vsync event has been received, and (2) there was // at least one connection interested in receiving it when we started waiting. Vector< sp<EventThread::Connection> > EventThread::waitForEvent( DisplayEventReceiver::Event* event) { Mutex::Autolock _l(mLock); Vector< sp<EventThread::Connection> > signalConnections; do { bool eventPending = false; bool waitForVSync = false; size_t vsyncCount = 0; nsecs_t timestamp = 0; for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { timestamp = mVSyncEvent[i].header.timestamp; if (timestamp) { // we have a vsync event to dispatch *event = mVSyncEvent[i]; mVSyncEvent[i].header.timestamp = 0; vsyncCount = mVSyncEvent[i].vsync.count; break; } } if (!timestamp) { // no vsync event, see if there are some other event eventPending = !mPendingEvents.isEmpty(); if (eventPending) { // we have some other event to dispatch *event = mPendingEvents[0]; mPendingEvents.removeAt(0); } } // find out connections waiting for events size_t count = mDisplayEventConnections.size(); for (size_t i=0 ; i<count ; i++) { sp<Connection> connection(mDisplayEventConnections[i].promote()); if (connection != NULL) { bool added = false; if (connection->count >= 0) { // we need vsync events because at least // one connection is waiting for it waitForVSync = true; if (timestamp) { // we consume the event only if it's time // (ie: we received a vsync event) if (connection->count == 0) { // fired this time around connection->count = -1; signalConnections.add(connection); added = true; } else if (connection->count == 1 || (vsyncCount % connection->count) == 0) { // continuous event, and time to report it signalConnections.add(connection); added = true; } } } if (eventPending && !timestamp && !added) { // we don't have a vsync event to process // (timestamp==0), but we have some pending // messages. signalConnections.add(connection); } } else { // we couldn't promote this reference, the connection has // died, so clean-up! mDisplayEventConnections.removeAt(i); --i; --count; } } // Here we figure out if we need to enable or disable vsyncs if (timestamp && !waitForVSync) { // we received a VSYNC but we have no clients // don't report it, and disable VSYNC events disableVSyncLocked(); } else if (!timestamp && waitForVSync) { // we have at least one client, so we want vsync enabled // (TODO: this function is called right after we finish // notifying clients of a vsync, so this call will be made // at the vsync rate, e.g. 60fps. If we can accurately // track the current state we could avoid making this call // so often.) enableVSyncLocked(); } // note: !timestamp implies signalConnections.isEmpty(), because we // don't populate signalConnections if there's no vsync pending if (!timestamp && !eventPending) { // wait for something to happen if (waitForVSync) { // This is where we spend most of our time, waiting // for vsync events and new client registrations. // // If the screen is off, we can't use h/w vsync, so we // use a 16ms timeout instead. It doesn't need to be // precise, we just need to keep feeding our clients. // // We don't want to stall if there's a driver bug, so we // use a (long) timeout when waiting for h/w vsync, and // generate fake events when necessary. bool softwareSync = mUseSoftwareVSync; nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000); if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) { if (!softwareSync) { ALOGW("Timed out waiting for hw vsync; faking it"); } // FIXME: how do we decide which display id the fake // vsync came from ? mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY; mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); mVSyncEvent[0].vsync.count++; } } else { // Nobody is interested in vsync, so we just want to sleep. // h/w vsync should be disabled, so this will wait until we // get a new connection, or an existing connection becomes // interested in receiving vsync again. mCondition.wait(mLock); } } } while (signalConnections.isEmpty()); // here we're guaranteed to have a timestamp and some connections to signal // (The connections might have dropped out of mDisplayEventConnections // while we were asleep, but we'll still have strong references to them.) return signalConnections; }
void LegacyCACFLayerTreeHost::render(const Vector<CGRect>& windowDirtyRects) { ASSERT(m_d3dDevice); if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) { // We can't reset the device right now. Try again soon. renderSoon(); return; } CGRect bounds = this->bounds(); // Give the renderer some space to use. This needs to be valid until the // wkCACFContextFinishUpdate() call below. char space[4096]; if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), CACurrentMediaTime(), bounds, windowDirtyRects.data(), windowDirtyRects.size())) return; HRESULT err = S_OK; CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity(); do { // FIXME: don't need to clear dirty region if layer tree is opaque. WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); if (!e) break; Vector<D3DRECT, 64> rects; for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { D3DRECT rect; rect.x1 = r->origin.x; rect.x2 = rect.x1 + r->size.width; rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height); rect.y2 = rect.y1 + r->size.height; rects.append(rect); } wkCACFUpdateRectEnumeratorRelease(e); timeToNextRender = wkCACFContextGetNextUpdateTime(m_context); if (rects.isEmpty()) break; m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); m_d3dDevice->BeginScene(); wkCACFContextRenderUpdate(m_context); m_d3dDevice->EndScene(); err = m_d3dDevice->Present(0, 0, 0, 0); if (err == D3DERR_DEVICELOST) { wkCACFContextAddUpdateRect(m_context, bounds); if (!resetDevice(LostDevice)) { // We can't reset the device right now. Try again soon. renderSoon(); return; } } } while (err == D3DERR_DEVICELOST); wkCACFContextFinishUpdate(m_context); #ifndef NDEBUG if (m_printTree) rootLayer()->printTree(); #endif // If timeToNextRender is not infinity, it means animations are running, so queue up to render again if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity()) renderSoon(); }
inline static bool ancestorsCrossShadowBoundaries(const Vector<EventContext>& ancestors) { return ancestors.isEmpty() || ancestors.first().node() == ancestors.last().node(); }
bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const SimpleFontData* fontData, GlyphBuffer* glyphBuffer) { // Determine the string for this item. const UChar* str = cp + m_items[i].iCharPos; int len = m_items[i+1].iCharPos - m_items[i].iCharPos; SCRIPT_ITEM item = m_items[i]; // Set up buffers to hold the results of shaping the item. Vector<WORD> glyphs; Vector<WORD> clusters; Vector<SCRIPT_VISATTR> visualAttributes; clusters.resize(len); // Shape the item. // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs. // Apparently this is a good size to avoid having to make repeated calls to ScriptShape. glyphs.resize(1.5 * len + 16); visualAttributes.resize(glyphs.size()); if (!shape(str, len, item, fontData, glyphs, clusters, visualAttributes)) return true; // We now have a collection of glyphs. Vector<GOFFSET> offsets; Vector<int> advances; offsets.resize(glyphs.size()); advances.resize(glyphs.size()); int glyphCount = 0; HRESULT placeResult = ScriptPlace(0, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); if (placeResult == E_PENDING) { // The script cache isn't primed with enough info yet. We need to select our HFONT into // a DC and pass the DC in to ScriptPlace. HDC hdc = GetDC(0); HFONT hfont = fontData->platformData().hfont(); HFONT oldFont = (HFONT)SelectObject(hdc, hfont); placeResult = ScriptPlace(hdc, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); } if (FAILED(placeResult) || glyphs.isEmpty()) return true; // Convert all chars that should be treated as spaces to use the space glyph. // We also create a map that allows us to quickly go from space glyphs back to their corresponding characters. Vector<int> spaceCharacters(glyphs.size()); spaceCharacters.fill(-1); const float cLogicalScale = fontData->platformData().useGDI() ? 1.0f : 32.0f; unsigned logicalSpaceWidth = fontData->spaceWidth() * cLogicalScale; float spaceWidth = fontData->spaceWidth(); for (int k = 0; k < len; k++) { UChar ch = *(str + k); bool treatAsSpace = Font::treatAsSpace(ch); bool treatAsZeroWidthSpace = ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch); if (treatAsSpace || treatAsZeroWidthSpace) { // Substitute in the space glyph at the appropriate place in the glyphs // array. glyphs[clusters[k]] = fontData->spaceGlyph(); advances[clusters[k]] = treatAsSpace ? logicalSpaceWidth : 0; if (treatAsSpace) spaceCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; } } // Populate our glyph buffer with this information. bool hasExtraSpacing = m_font.letterSpacing() || m_font.wordSpacing() || m_padding; float leftEdge = m_runWidthSoFar; for (unsigned k = 0; k < glyphs.size(); k++) { Glyph glyph = glyphs[k]; float advance = advances[k] / cLogicalScale; float offsetX = offsets[k].du / cLogicalScale; float offsetY = offsets[k].dv / cLogicalScale; // Match AppKit's rules for the integer vs. non-integer rendering modes. float roundedAdvance = roundf(advance); if (!m_font.isPrinterFont() && !fontData->isSystemFont()) { advance = roundedAdvance; offsetX = roundf(offsetX); offsetY = roundf(offsetY); } advance += fontData->syntheticBoldOffset(); if (hasExtraSpacing) { // If we're a glyph with an advance, go ahead and add in letter-spacing. // That way we weed out zero width lurkers. This behavior matches the fast text code path. if (advance && m_font.letterSpacing()) advance += m_font.letterSpacing(); // Handle justification and word-spacing. int characterIndex = spaceCharacters[k]; // characterIndex is left at the initial value of -1 for glyphs that do not map back to treated-as-space characters. if (characterIndex != -1) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { // Use leftover padding if not evenly divisible by number of spaces. if (m_padding < m_padPerSpace) { advance += m_padding; m_padding = 0; } else { m_padding -= m_padPerSpace; advance += m_padPerSpace; } } // Account for word-spacing. if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) advance += m_font.wordSpacing(); } } m_runWidthSoFar += advance; // FIXME: We need to take the GOFFSETS for combining glyphs and store them in the glyph buffer // as well, so that when the time comes to draw those glyphs, we can apply the appropriate // translation. if (glyphBuffer) { FloatSize size(offsetX, -offsetY); glyphBuffer->add(glyph, fontData, advance, &size); } FloatRect glyphBounds = fontData->boundsForGlyph(glyph); glyphBounds.move(m_glyphOrigin.x(), m_glyphOrigin.y()); m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); m_glyphOrigin.move(advance + offsetX, -offsetY); // Mutate the glyph array to contain our altered advances. if (m_computingOffsetPosition) advances[k] = advance; } while (m_computingOffsetPosition && m_offsetX >= leftEdge && m_offsetX < m_runWidthSoFar) { // The position is somewhere inside this run. int trailing = 0; ScriptXtoCP(m_offsetX - leftEdge, clusters.size(), glyphs.size(), clusters.data(), visualAttributes.data(), advances.data(), &item.a, &m_offsetPosition, &trailing); if (trailing && m_includePartialGlyphs && m_offsetPosition < len - 1) { m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; m_offsetX += m_run.rtl() ? -trailing : trailing; } else { m_computingOffsetPosition = false; m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; if (trailing && m_includePartialGlyphs) m_offsetPosition++; return false; } } return true; }
PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String& topURLString, const String& topTitle, const String& topOriginalURLString, Decoder& decoder) { // Since the data stream is not trusted, the decode has to be non-recursive. // We don't want bad data to cause a stack overflow. uint32_t version; if (!decoder.decodeUInt32(version)) return 0; if (version != backForwardTreeEncodingVersion) return 0; String urlString = topURLString; String title = topTitle; String originalURLString = topOriginalURLString; Vector<DecodeRecursionStackElement, 16> recursionStack; recurse: RefPtr<HistoryItem> node = create(urlString, title, 0); node->setOriginalURLString(originalURLString); title = String(); uint64_t size; if (!decoder.decodeUInt64(size)) return 0; size_t i; RefPtr<HistoryItem> child; for (i = 0; i < size; ++i) { if (!decoder.decodeString(originalURLString)) return 0; if (!decoder.decodeString(urlString)) return 0; recursionStack.append(DecodeRecursionStackElement(node.release(), i, size)); goto recurse; resume: node->m_children.append(child.release()); } if (!decoder.decodeInt64(node->m_documentSequenceNumber)) return 0; if (!decoder.decodeUInt64(size)) return 0; for (i = 0; i < size; ++i) { String state; if (!decoder.decodeString(state)) return 0; node->m_documentState.append(state); } if (!decoder.decodeString(node->m_formContentType)) return 0; bool hasFormData; if (!decoder.decodeBool(hasFormData)) return 0; if (hasFormData) { node->m_formData = FormData::decode(decoder); if (!node->m_formData) return 0; } if (!decoder.decodeInt64(node->m_itemSequenceNumber)) return 0; if (!decoder.decodeString(node->m_referrer)) return 0; int32_t x; if (!decoder.decodeInt32(x)) return 0; int32_t y; if (!decoder.decodeInt32(y)) return 0; node->m_scrollPoint = IntPoint(x, y); if (!decoder.decodeFloat(node->m_pageScaleFactor)) return 0; bool hasStateObject; if (!decoder.decodeBool(hasStateObject)) return 0; if (hasStateObject) { Vector<uint8_t> bytes; if (!decoder.decodeBytes(bytes)) return 0; node->m_stateObject = SerializedScriptValue::adopt(bytes); } if (!decoder.decodeString(node->m_target)) return 0; // Simulate recursion with our own stack. if (!recursionStack.isEmpty()) { DecodeRecursionStackElement& element = recursionStack.last(); child = node.release(); node = element.node.release(); i = element.i; size = element.size; recursionStack.removeLast(); goto resume; } return node.release(); }
bool JSHTMLCollection::canGetItemsForName(ExecState*, HTMLCollection* collection, const Identifier& propertyName) { Vector<RefPtr<Node> > namedItems; collection->namedItems(identifierToAtomicString(propertyName), namedItems); return !namedItems.isEmpty(); }
bool SizesCalcParser::calcToReversePolishNotation(CSSParserTokenIterator start, CSSParserTokenIterator end) { // This method implements the shunting yard algorithm, to turn the calc syntax into a reverse polish notation. // http://en.wikipedia.org/wiki/Shunting-yard_algorithm Vector<CSSParserToken> stack; for (CSSParserTokenIterator it = start; it != end; ++it) { CSSParserTokenType type = it->type(); switch (type) { case NumberToken: appendNumber(*it); break; case DimensionToken: if (!CSSPrimitiveValue::isLength(it->unitType()) || !appendLength(*it)) return false; break; case DelimiterToken: if (!handleOperator(stack, *it)) return false; break; case FunctionToken: if (it->value() != "calc") return false; // "calc(" is the same as "(" case LeftParenthesisToken: // If the token is a left parenthesis, then push it onto the stack. stack.append(*it); break; case RightParenthesisToken: // If the token is a right parenthesis: // Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue. while (!stack.isEmpty() && stack.last().type() != LeftParenthesisToken && stack.last().type() != FunctionToken) { appendOperator(stack.last()); stack.removeLast(); } // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. if (stack.isEmpty()) return false; // Pop the left parenthesis from the stack, but not onto the output queue. stack.removeLast(); break; case CommentToken: case WhitespaceToken: case EOFToken: break; case HashToken: case UrlToken: case BadUrlToken: case PercentageToken: case UnicodeRangeToken: case IdentToken: case CommaToken: case ColonToken: case SemicolonToken: case LeftBraceToken: case LeftBracketToken: case RightBraceToken: case RightBracketToken: case StringToken: case BadStringToken: return false; } } // When there are no more tokens to read: // While there are still operator tokens in the stack: while (!stack.isEmpty()) { // If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses. CSSParserTokenType type = stack.last().type(); if (type == LeftParenthesisToken || type == FunctionToken) return false; // Pop the operator onto the output queue. appendOperator(stack.last()); stack.removeLast(); } return true; }
FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName) { if (m_fontFaces.isEmpty()) { if (familyName.startsWith("-webkit-")) return fontDataForGenericFamily(m_document, fontDescription, familyName); if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont()) return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard"); return 0; } String family = familyName.string(); Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family); // If no face was found, then return 0 and let the OS come up with its best match for the name. if (!familyFontFaces || familyFontFaces->isEmpty()) { // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our // settings. if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont()) return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard"); return fontDataForGenericFamily(m_document, fontDescription, familyName); } OwnPtr<HashMap<unsigned, RefPtr<CSSSegmentedFontFace> > >& segmentedFontFaceCache = m_fonts.add(family, nullptr).first->second; if (!segmentedFontFaceCache) segmentedFontFaceCache = adoptPtr(new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >); FontTraitsMask traitsMask = fontDescription.traitsMask(); RefPtr<CSSSegmentedFontFace>& face = segmentedFontFaceCache->add(traitsMask, 0).first->second; if (!face) { face = CSSSegmentedFontFace::create(this); // Collect all matching faces and sort them in order of preference. Vector<CSSFontFace*, 32> candidateFontFaces; for (int i = familyFontFaces->size() - 1; i >= 0; --i) { CSSFontFace* candidate = familyFontFaces->at(i).get(); unsigned candidateTraitsMask = candidate->traitsMask(); if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask)) continue; if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask)) continue; #if ENABLE(SVG_FONTS) // For SVG Fonts that specify that they only support the "normal" variant, we will assume they are incapable // of small-caps synthesis and just ignore the font face as a candidate. if (candidate->hasSVGFontFaceSource() && (traitsMask & FontVariantSmallCapsMask) && !(candidateTraitsMask & FontVariantSmallCapsMask)) continue; #endif candidateFontFaces.append(candidate); } if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) { unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size(); for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) { CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get(); unsigned candidateTraitsMask = candidate->traitsMask(); if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask)) continue; if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask)) continue; candidateFontFaces.append(candidate); } } desiredTraitsMaskForComparison = traitsMask; stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces); unsigned numCandidates = candidateFontFaces.size(); for (unsigned i = 0; i < numCandidates; ++i) face->appendFontFace(candidateFontFaces[i]); } // We have a face. Ask it for a font data. If it cannot produce one, it will fail, and the OS will take over. return face->getFontData(fontDescription); }
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(); }
void CommandLine::parseArguments(int argc, char** argv) { int i = 1; bool needToDumpOptions = false; bool needToExit = false; for (; i < argc; ++i) { const char* arg = argv[i]; if (!strcmp(arg, "-f")) { if (++i == argc) printUsageStatement(); m_scripts.append(Script(true, argv[i])); continue; } if (!strcmp(arg, "-e")) { if (++i == argc) printUsageStatement(); m_scripts.append(Script(false, argv[i])); continue; } if (!strcmp(arg, "-i")) { m_interactive = true; continue; } if (!strcmp(arg, "-d")) { m_dump = true; continue; } if (!strcmp(arg, "-p")) { if (++i == argc) printUsageStatement(); m_profile = true; m_profilerOutput = argv[i]; continue; } if (!strcmp(arg, "-s")) { #if HAVE(SIGNAL_H) signal(SIGILL, _exit); signal(SIGFPE, _exit); signal(SIGBUS, _exit); signal(SIGSEGV, _exit); #endif continue; } if (!strcmp(arg, "-x")) { m_exitCode = true; continue; } if (!strcmp(arg, "--")) { ++i; break; } if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) printUsageStatement(true); if (!strcmp(arg, "--options")) { needToDumpOptions = true; needToExit = true; continue; } if (!strcmp(arg, "--dumpOptions")) { needToDumpOptions = true; continue; } // See if the -- option is a JSC VM option. // NOTE: At this point, we know that the arg starts with "--". Skip it. if (JSC::Options::setOption(&arg[2])) { // The arg was recognized as a VM option and has been parsed. continue; // Just continue with the next arg. } // This arg is not recognized by the VM nor by jsc. Pass it on to the // script. m_scripts.append(Script(true, argv[i])); } if (m_scripts.isEmpty()) m_interactive = true; for (; i < argc; ++i) m_arguments.append(argv[i]); if (needToDumpOptions) JSC::Options::dumpAllOptions(stderr); if (needToExit) exit(EXIT_SUCCESS); }
AJValue LiteralParser::parse(ParserState initialState) { ParserState state = initialState; MarkedArgumentBuffer objectStack; AJValue lastValue; Vector<ParserState, 16> stateStack; Vector<Identifier, 16> identifierStack; while (1) { switch(state) { startParseArray: case StartParseArray: { AJArray* array = constructEmptyArray(m_exec); objectStack.append(array); // fallthrough } doParseArrayStartExpression: case DoParseArrayStartExpression: { TokenType lastToken = m_lexer.currentToken().type; if (m_lexer.next() == TokRBracket) { if (lastToken == TokComma) return AJValue(); m_lexer.next(); lastValue = objectStack.last(); objectStack.removeLast(); break; } stateStack.append(DoParseArrayEndExpression); goto startParseExpression; } case DoParseArrayEndExpression: { asArray(objectStack.last())->push(m_exec, lastValue); if (m_lexer.currentToken().type == TokComma) goto doParseArrayStartExpression; if (m_lexer.currentToken().type != TokRBracket) return AJValue(); m_lexer.next(); lastValue = objectStack.last(); objectStack.removeLast(); break; } startParseObject: case StartParseObject: { AJObject* object = constructEmptyObject(m_exec); objectStack.append(object); TokenType type = m_lexer.next(); if (type == TokString) { Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); // Check for colon if (m_lexer.next() != TokColon) return AJValue(); m_lexer.next(); identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); stateStack.append(DoParseObjectEndExpression); goto startParseExpression; } else if (type != TokRBrace) return AJValue(); m_lexer.next(); lastValue = objectStack.last(); objectStack.removeLast(); break; } doParseObjectStartExpression: case DoParseObjectStartExpression: { TokenType type = m_lexer.next(); if (type != TokString) return AJValue(); Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); // Check for colon if (m_lexer.next() != TokColon) return AJValue(); m_lexer.next(); identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); stateStack.append(DoParseObjectEndExpression); goto startParseExpression; } case DoParseObjectEndExpression: { asObject(objectStack.last())->putDirect(identifierStack.last(), lastValue); identifierStack.removeLast(); if (m_lexer.currentToken().type == TokComma) goto doParseObjectStartExpression; if (m_lexer.currentToken().type != TokRBrace) return AJValue(); m_lexer.next(); lastValue = objectStack.last(); objectStack.removeLast(); break; } startParseExpression: case StartParseExpression: { switch (m_lexer.currentToken().type) { case TokLBracket: goto startParseArray; case TokLBrace: goto startParseObject; case TokString: { Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); m_lexer.next(); lastValue = jsString(m_exec, stringToken.stringToken); break; } case TokNumber: { Lexer::LiteralParserToken numberToken = m_lexer.currentToken(); m_lexer.next(); lastValue = jsNumber(m_exec, numberToken.numberToken); break; } case TokNull: m_lexer.next(); lastValue = jsNull(); break; case TokTrue: m_lexer.next(); lastValue = jsBoolean(true); break; case TokFalse: m_lexer.next(); lastValue = jsBoolean(false); break; default: // Error return AJValue(); } break; } case StartParseStatement: { switch (m_lexer.currentToken().type) { case TokLBracket: case TokNumber: case TokString: goto startParseExpression; case TokLParen: { m_lexer.next(); stateStack.append(StartParseStatementEndStatement); goto startParseExpression; } default: return AJValue(); } } case StartParseStatementEndStatement: { ASSERT(stateStack.isEmpty()); if (m_lexer.currentToken().type != TokRParen) return AJValue(); if (m_lexer.next() == TokEnd) return lastValue; return AJValue(); } default: ASSERT_NOT_REACHED(); } if (stateStack.isEmpty()) return lastValue; state = stateStack.last(); stateStack.removeLast(); continue; } }
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 = URL(URL(), url); if (!m_url.isValid()) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "URL has fragment component " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(m_url)) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "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 (is<Document>(*scriptExecutionContext())) { Document& document = downcast<Document>(*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 = ThreadableWebSocketChannel::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 (auto& protocol : protocols) { if (!isValidProtocolString(protocol)) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } HashSet<String> visited; for (auto& protocol : protocols) { if (!visited.add(protocol).isNewEntry) { scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; ec = SYNTAX_ERR; return; } } if (is<Document>(*scriptExecutionContext())) { Document& document = downcast<Document>(*scriptExecutionContext()); if (!document.frame()->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) { // Balanced by the call to ActiveDOMObject::unsetPendingActivity() in WebSocket::stop(). ActiveDOMObject::setPendingActivity(this); // We must block this connection. Instead of throwing an exception, we indicate this // using the error event. But since this code executes as part of the WebSocket's // constructor, we have to wait until the constructor has completed before firing the // event; otherwise, users can't connect to the event. RunLoop::main().dispatch([this]() { dispatchEvent(Event::create(eventNames().errorEvent, false, false)); stop(); }); return; } } String protocolString; if (!protocols.isEmpty()) protocolString = joinStrings(protocols, subProtocolSeperator()); m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); }
void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) { Editor::RevealSelectionScope revealSelectionScope(&editor()); // Updates styles before setting selection for composition to prevent // inserting the previous composition text into text nodes oddly. // See https://bugs.webkit.org/show_bug.cgi?id=46868 frame().document()->updateLayoutTreeIfNeeded(); selectComposition(); if (frame().selection().isNone()) return; if (Element* target = frame().document()->focusedElement()) { // Dispatch an appropriate composition event to the focused node. // We check the composition status and choose an appropriate composition event since this // function is used for three purposes: // 1. Starting a new composition. // Send a compositionstart and a compositionupdate event when this function creates // a new composition node, i.e. // !hasComposition() && !text.isEmpty(). // Sending a compositionupdate event at this time ensures that at least one // compositionupdate event is dispatched. // 2. Updating the existing composition node. // Send a compositionupdate event when this function updates the existing composition // node, i.e. hasComposition() && !text.isEmpty(). // 3. Canceling the ongoing composition. // Send a compositionend event when function deletes the existing composition node, i.e. // !hasComposition() && test.isEmpty(). RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; if (!hasComposition()) { // We should send a compositionstart event only when the given text is not empty because this // function doesn't create a composition node when the text is empty. if (!text.isEmpty()) { target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, frame().domWindow(), frame().selectedText())); event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text); } } else { if (!text.isEmpty()) event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text); else event = CompositionEvent::create(EventTypeNames::compositionend, frame().domWindow(), text); } if (event.get()) target->dispatchEvent(event); } // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input // will delete the old composition with an optimized replace operation. if (text.isEmpty()) { ASSERT(frame().document()); TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking); } clear(); if (text.isEmpty()) return; ASSERT(frame().document()); TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); // Find out what node has the composition now. Position base = mostForwardCaretPosition(frame().selection().base()); Node* baseNode = base.anchorNode(); if (!baseNode || !baseNode->isTextNode()) return; Position extent = frame().selection().extent(); Node* extentNode = extent.anchorNode(); if (baseNode != extentNode) return; unsigned extentOffset = extent.computeOffsetInContainerNode(); unsigned baseOffset = base.computeOffsetInContainerNode(); if (baseOffset + text.length() != extentOffset) return; m_isDirty = true; m_hasComposition = true; if (!m_compositionRange) m_compositionRange = Range::create(baseNode->document()); m_compositionRange->setStart(baseNode, baseOffset); m_compositionRange->setEnd(baseNode, extentOffset); if (baseNode->layoutObject()) baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); unsigned start = std::min(baseOffset + selectionStart, extentOffset); unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset); RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Downstream, SelectionDirectionalMode::NonDirectional, NotUserTriggered); if (underlines.isEmpty()) { frame().document()->markers().addCompositionMarker(m_compositionRange->startPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTheme::theme().platformDefaultCompositionBackgroundColor()); return; } for (const auto& underline : underlines) { unsigned underlineStart = baseOffset + underline.startOffset; unsigned underlineEnd = baseOffset + underline.endOffset; EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); if (ephemeralLineRange.isNull()) continue; frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thick, underline.backgroundColor); } }