// Returns two point ranges (<left, width> pairs) at row |canvasY| which belong // to |src| but not |dst|. A range is empty if its width is 0. inline void findBlendRangeAtRow(const blink::IntRect& src, const blink::IntRect& dst, int canvasY, int& left1, int& width1, int& left2, int& width2) { SECURITY_DCHECK(canvasY >= src.y() && canvasY < src.maxY()); left1 = -1; width1 = 0; left2 = -1; width2 = 0; if (canvasY < dst.y() || canvasY >= dst.maxY() || src.x() >= dst.maxX() || src.maxX() <= dst.x()) { left1 = src.x(); width1 = src.width(); return; } if (src.x() < dst.x()) { left1 = src.x(); width1 = dst.x() - src.x(); } if (src.maxX() > dst.maxX()) { left2 = dst.maxX(); width2 = src.maxX() - dst.maxX(); } }
size_t SharedBuffer::getSomeDataInternal(const char*& someData, size_t position) const { size_t totalSize = size(); if (position >= totalSize) { someData = 0; return 0; } SECURITY_DCHECK(position < m_size); size_t consecutiveSize = m_buffer.size(); if (position < consecutiveSize) { someData = m_buffer.data() + position; return consecutiveSize - position; } position -= consecutiveSize; size_t segments = m_segments.size(); size_t maxSegmentedSize = segments * kSegmentSize; size_t segment = segmentIndex(position); if (segment < segments) { size_t bytesLeft = totalSize - consecutiveSize; size_t segmentedSize = std::min(maxSegmentedSize, bytesLeft); size_t positionInSegment = offsetInSegment(position); someData = m_segments[segment] + positionInSegment; return segment == segments - 1 ? segmentedSize - position : kSegmentSize - positionInSegment; } ASSERT_NOT_REACHED(); return 0; }
bool StyleSheetContents::wrapperInsertRule(StyleRuleBase* rule, unsigned index) { ASSERT(m_isMutable); SECURITY_DCHECK(index <= ruleCount()); if (index < m_importRules.size() || (index == m_importRules.size() && rule->isImportRule())) { // Inserting non-import rule before @import is not allowed. if (!rule->isImportRule()) return false; StyleRuleImport* importRule = toStyleRuleImport(rule); if (importRule->mediaQueries()) setHasMediaQueries(); m_importRules.insert(index, importRule); m_importRules[index]->setParentStyleSheet(this); m_importRules[index]->requestStyleSheet(); // FIXME: Stylesheet doesn't actually change meaningfully before the // imported sheets are loaded. return true; } // Inserting @import rule after a non-import rule is not allowed. if (rule->isImportRule()) return false; index -= m_importRules.size(); if (index < m_namespaceRules.size() || (index == m_namespaceRules.size() && rule->isNamespaceRule())) { // Inserting non-namespace rules other than import rule before @namespace is // not allowed. if (!rule->isNamespaceRule()) return false; // Inserting @namespace rule when rules other than import/namespace/charset // are present is not allowed. if (!m_childRules.isEmpty()) return false; StyleRuleNamespace* namespaceRule = toStyleRuleNamespace(rule); m_namespaceRules.insert(index, namespaceRule); // For now to be compatible with IE and Firefox if namespace rule with same // prefix is added irrespective of adding the rule at any index, last added // rule's value is considered. // TODO ([email protected]): As per spec last valid rule should be // considered, which means if namespace rule is added in the middle of // existing namespace rules, rule which comes later in rule list with same // prefix needs to be considered. parserAddNamespace(namespaceRule->prefix(), namespaceRule->uri()); return true; } if (rule->isNamespaceRule()) return false; index -= m_namespaceRules.size(); m_childRules.insert(index, rule); return true; }
bool StyleSheetContents::wrapperDeleteRule(unsigned index) { ASSERT(m_isMutable); SECURITY_DCHECK(index < ruleCount()); if (index < m_importRules.size()) { m_importRules[index]->clearParentStyleSheet(); if (m_importRules[index]->isFontFaceRule()) notifyRemoveFontFaceRule(toStyleRuleFontFace(m_importRules[index].get())); m_importRules.remove(index); return true; } index -= m_importRules.size(); if (index < m_namespaceRules.size()) { if (!m_childRules.isEmpty()) return false; m_namespaceRules.remove(index); return true; } index -= m_namespaceRules.size(); if (m_childRules[index]->isFontFaceRule()) notifyRemoveFontFaceRule(toStyleRuleFontFace(m_childRules[index].get())); m_childRules.remove(index); return true; }
inline size_t SearchBuffer::search(size_t& start) { size_t size = m_buffer.size(); if (m_atBreak) { if (!size) return 0; } else { if (size != m_buffer.capacity()) return 0; } m_textSearcher->setText(m_buffer.data(), size); m_textSearcher->setOffset(m_prefixLength); MatchResult match; nextMatch: if (!m_textSearcher->nextMatchResult(match)) return 0; // Matches that start in the overlap area are only tentative. // The same match may appear later, matching more characters, // possibly including a combining character that's not yet in the buffer. if (!m_atBreak && match.start >= size - m_overlap) { size_t overlap = m_overlap; if (m_options & AtWordStarts) { // Ensure that there is sufficient context before matchStart the next time // around for determining if it is at a word boundary. int wordBoundaryContextStart = match.start; U16_BACK_1(m_buffer.data(), 0, wordBoundaryContextStart); wordBoundaryContextStart = startOfLastWordBoundaryContext( m_buffer.data(), wordBoundaryContextStart); overlap = std::min(size - 1, std::max(overlap, size - wordBoundaryContextStart)); } memcpy(m_buffer.data(), m_buffer.data() + size - overlap, overlap * sizeof(UChar)); m_prefixLength -= std::min(m_prefixLength, size - overlap); m_buffer.shrink(overlap); return 0; } SECURITY_DCHECK(match.start + match.length <= size); // If this match is "bad", move on to the next match. if (isBadMatch(m_buffer.data() + match.start, match.length) || ((m_options & AtWordStarts) && !isWordStartMatch(match.start, match.length))) { goto nextMatch; } size_t newSize = size - (match.start + 1); memmove(m_buffer.data(), m_buffer.data() + match.start + 1, newSize * sizeof(UChar)); m_prefixLength -= std::min<size_t>(m_prefixLength, match.start + 1); m_buffer.shrink(newSize); start = size - match.start; return match.length; }
void WebSharedWorkerImpl::connectTask(WebMessagePortChannelUniquePtr channel, ExecutionContext* context) { // Wrap the passed-in channel in a MessagePort, and send it off via a connect // event. MessagePort* port = MessagePort::create(*context); port->entangle(std::move(channel)); WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); SECURITY_DCHECK(workerGlobalScope->isSharedWorkerGlobalScope()); workerGlobalScope->dispatchEvent(createConnectEvent(port)); }
int TextFinder::selectFindMatch(unsigned index, WebRect* selectionRect) { SECURITY_DCHECK(index < m_findMatchesCache.size()); Range* range = m_findMatchesCache[index].m_range; if (!range->boundaryPointsValid() || !range->startContainer()->isConnected()) return -1; // Check if the match is already selected. if (!m_currentActiveMatchFrame || !m_activeMatch || !areRangesEqual(m_activeMatch.get(), range)) { m_activeMatchIndex = m_findMatchesCache[index].m_ordinal - 1; // Set this frame as the active frame (the one with the active highlight). m_currentActiveMatchFrame = true; ownerFrame().viewImpl()->setFocusedFrame(&ownerFrame()); if (m_activeMatch) setMarkerActive(m_activeMatch.get(), false); m_activeMatch = range; setMarkerActive(m_activeMatch.get(), true); // Clear any user selection, to make sure Find Next continues on from the // match we just activated. ownerFrame().frame()->selection().clear(); // Make sure no node is focused. See http://crbug.com/38700. ownerFrame().frame()->document()->clearFocusedElement(); } IntRect activeMatchRect; IntRect activeMatchBoundingBox = enclosingIntRect( LayoutObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())); if (!activeMatchBoundingBox.isEmpty()) { if (m_activeMatch->firstNode() && m_activeMatch->firstNode()->layoutObject()) { m_activeMatch->firstNode()->layoutObject()->scrollRectToVisible( LayoutRect(activeMatchBoundingBox), ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, UserScroll); } // Zoom to the active match. activeMatchRect = ownerFrame().frameView()->contentsToRootFrame(activeMatchBoundingBox); ownerFrame().viewImpl()->zoomToFindInPageRect(activeMatchRect); } if (selectionRect) *selectionRect = activeMatchRect; return m_activeMatchIndex + 1; }
static inline unsigned weightScore(FontTraits desired, FontTraits candidate) { static_assert(FontWeight100 == 0 && FontWeight900 - FontWeight100 == 8, "Enumeration values need to match lookup table."); static const unsigned scoreLookupSize = FontWeight900 + 1; // https://drafts.csswg.org/css-fonts/#font-style-matching // "..if the desired weight is available that face matches. " static const unsigned weightScoreLookup[scoreLookupSize][scoreLookupSize] = { // "If the desired weight is less than 400, weights below the desired // weight are checked in descending order followed by weights above the // desired weight in ascending order until a match is found." {9, 8, 7, 6, 5, 4, 3, 2, 1}, // FontWeight100 desired {8, 9, 7, 6, 5, 4, 3, 2, 1}, // FontWeight200 desired {7, 8, 9, 6, 5, 4, 3, 2, 1}, // FontWeight300 desired // "If the desired weight is 400, 500 is checked first and then the rule // for desired weights less than 400 is used." {5, 6, 7, 9, 8, 4, 3, 2, 1}, // FontWeight400 desired // "If the desired weight is 500, 400 is checked first and then the rule // for desired weights less than 400 is used." {5, 6, 7, 8, 9, 4, 3, 2, 1}, // FontWeight500 desired // "If the desired weight is greater than 500, weights above the desired // weight are checked in ascending order followed by weights below the // desired weight in descending order until a match is found." {1, 2, 3, 4, 5, 9, 8, 7, 6}, // FontWeight600 desired {1, 2, 3, 4, 5, 6, 9, 8, 7}, // FontWeight700 desired {1, 2, 3, 4, 5, 6, 7, 9, 8}, // FontWeight800 desired {1, 2, 3, 4, 5, 6, 7, 8, 9} // FontWeight900 desired }; unsigned desiredScoresLookup = static_cast<unsigned>(desired.weight()); unsigned candidateScoreLookup = static_cast<unsigned>(candidate.weight()); SECURITY_DCHECK(desiredScoresLookup < scoreLookupSize); SECURITY_DCHECK(candidateScoreLookup < scoreLookupSize); return weightScoreLookup[desiredScoresLookup][candidateScoreLookup]; }
void HTMLFormattingElementList::swapTo(Element* oldElement, HTMLStackItem* newItem, const Bookmark& bookmark) { ASSERT(contains(oldElement)); ASSERT(!contains(newItem->element())); if (!bookmark.hasBeenMoved()) { ASSERT(bookmark.mark()->element() == oldElement); bookmark.mark()->replaceElement(newItem); return; } size_t index = bookmark.mark() - first(); SECURITY_DCHECK(index < size()); m_entries.insert(index + 1, newItem); remove(oldElement); }
StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const { SECURITY_DCHECK(index < ruleCount()); if (index < m_importRules.size()) return m_importRules[index].get(); index -= m_importRules.size(); if (index < m_namespaceRules.size()) return m_namespaceRules[index].get(); index -= m_namespaceRules.size(); return m_childRules[index].get(); }
static inline unsigned styleScore(FontTraits desired, FontTraits candidate) { static_assert(FontStyleNormal == 0 && FontStyleItalic == 2, "Enumeration values need to match lookup table."); unsigned styleScoreLookupTable[][FontStyleItalic + 1] = { // "If the value is normal, normal faces are checked first, then oblique // faces, then italic faces." // i.e. normal has the highest score, then oblique, then italic. {2, 1, 0}, // "If the value is oblique, oblique faces are checked first, then italic // faces and then normal faces." // i.e. normal gets the lowest score, oblique gets the highest, italic // second best. {0, 2, 1}, // "If the value of font-style is italic, italic faces are checked first, // then oblique, then normal faces" // i.e. normal gets the lowest score, oblique is second best, italic // highest. {0, 1, 2}}; SECURITY_DCHECK(desired.style() < FontStyleItalic + 1); SECURITY_DCHECK(candidate.style() < FontStyleItalic + 1); return styleScoreLookupTable[desired.style()][candidate.style()]; }
HTMLElement* CustomElement::createUndefinedElement(Document& document, const QualifiedName& tagName) { DCHECK(shouldCreateCustomElement(document, tagName)); HTMLElement* element; if (V0CustomElement::isValidName(tagName.localName()) && document.registrationContext()) { Element* v0element = document.registrationContext()->createCustomTagElement(document, tagName); SECURITY_DCHECK(v0element->isHTMLElement()); element = toHTMLElement(v0element); } else { element = HTMLElement::create(tagName, document); } element->setCustomElementState(CustomElementState::Undefined); return element; }
std::unique_ptr<InspectorTaskRunner::Task> InspectorTaskRunner::takeNextTask( InspectorTaskRunner::WaitMode waitMode) { MutexLocker lock(m_mutex); bool timedOut = false; static double infiniteTime = std::numeric_limits<double>::max(); double absoluteTime = waitMode == WaitForTask ? infiniteTime : 0.0; while (!m_killed && !timedOut && m_queue.isEmpty()) timedOut = !m_condition.timedWait(m_mutex, absoluteTime); ASSERT(!timedOut || absoluteTime != infiniteTime); if (m_killed || timedOut) return nullptr; SECURITY_DCHECK(!m_queue.isEmpty()); return m_queue.takeFirst(); }
void WorkerScriptLoader::loadSynchronously(ExecutionContext& executionContext, const KURL& url, CrossOriginRequestPolicy crossOriginRequestPolicy, WebAddressSpace creationAddressSpace) { m_url = url; ResourceRequest request(createResourceRequest(creationAddressSpace)); SECURITY_DCHECK(executionContext.isWorkerGlobalScope()); ThreadableLoaderOptions options; options.crossOriginRequestPolicy = crossOriginRequestPolicy; // FIXME: Should we add EnforceScriptSrcDirective here? options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy; ResourceLoaderOptions resourceLoaderOptions; resourceLoaderOptions.allowCredentials = AllowStoredCredentials; WorkerThreadableLoader::loadResourceSynchronously(toWorkerGlobalScope(executionContext), request, *this, options, resourceLoaderOptions); }
void RadioButtonGroupScope::removeButton(HTMLInputElement* element) { DCHECK_EQ(element->type(), InputTypeNames::radio); if (element->name().isEmpty()) return; if (!m_nameToGroupMap) return; RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); if (!group) return; group->remove(element); if (group->isEmpty()) { // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for // better performance. DCHECK(!group->isRequired()); SECURITY_DCHECK(!group->checkedButton()); } }
bool IndexedDBClientImpl::allowIndexedDB(ExecutionContext* context, const String& name) { DCHECK(context->isContextThread()); SECURITY_DCHECK(context->isDocument() || context->isWorkerGlobalScope()); if (context->isDocument()) { WebSecurityOrigin origin(context->getSecurityOrigin()); Document* document = toDocument(context); WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(document->frame()); if (!webFrame) return false; if (webFrame->contentSettingsClient()) return webFrame->contentSettingsClient()->allowIndexedDB(name, origin); return true; } WorkerGlobalScope& workerGlobalScope = *toWorkerGlobalScope(context); return WorkerContentSettingsClient::from(workerGlobalScope) ->allowIndexedDB(name); }
bool ICOImageDecoder::decodeAtIndex(size_t index) { SECURITY_DCHECK(index < m_dirEntries.size()); const IconDirectoryEntry& dirEntry = m_dirEntries[index]; const ImageType imageType = imageTypeAtIndex(index); if (imageType == Unknown) return false; // Not enough data to determine image type yet. if (imageType == BMP) { if (!m_bmpReaders[index]) { m_bmpReaders[index] = wrapUnique(new BMPImageReader(this, dirEntry.m_imageOffset, 0, true)); m_bmpReaders[index]->setData(m_data.get()); } // Update the pointer to the buffer as it could change after // m_frameBufferCache.resize(). m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]); m_frameSize = dirEntry.m_size; bool result = m_bmpReaders[index]->decodeBMP(false); m_frameSize = IntSize(); return result; } if (!m_pngDecoders[index]) { AlphaOption alphaOption = m_premultiplyAlpha ? AlphaPremultiplied : AlphaNotPremultiplied; m_pngDecoders[index] = wrapUnique( new PNGImageDecoder(alphaOption, m_colorSpaceOption, m_maxDecodedBytes, dirEntry.m_imageOffset)); setDataForPNGDecoderAtIndex(index); } // Fail if the size the PNGImageDecoder calculated does not match the size // in the directory. if (m_pngDecoders[index]->isSizeAvailable() && (m_pngDecoders[index]->size() != dirEntry.m_size)) return setFailed(); m_frameBufferCache[index] = *m_pngDecoders[index]->frameBufferAtIndex(0); m_frameBufferCache[index].setPremultiplyAlpha(m_premultiplyAlpha); return !m_pngDecoders[index]->failed() || setFailed(); }
bool SVGClipPainter::prepareEffect(const LayoutObject& target, const FloatRect& targetBoundingBox, const FloatRect& paintInvalidationRect, const FloatPoint& layerPositionOffset, GraphicsContext& context, ClipperState& clipperState) { DCHECK_EQ(clipperState, ClipperState::NotApplied); SECURITY_DCHECK(!m_clip.needsLayout()); m_clip.clearInvalidationMask(); if (paintInvalidationRect.isEmpty() || m_clip.hasCycle()) return false; SVGClipExpansionCycleHelper inClipExpansionChange(m_clip); AffineTransform animatedLocalTransform = toSVGClipPathElement(m_clip.element())->calculateAnimatedLocalTransform(); // When drawing a clip for non-SVG elements, the CTM does not include the zoom // factor. In this case, we need to apply the zoom scale explicitly - but // only for clips with userSpaceOnUse units (the zoom is accounted for // objectBoundingBox-resolved lengths). if (!target.isSVG() && m_clip.clipPathUnits() == SVGUnitTypes::kSvgUnitTypeUserspaceonuse) { DCHECK(m_clip.style()); animatedLocalTransform.scale(m_clip.style()->effectiveZoom()); } // First, try to apply the clip as a clipPath. Path clipPath; if (m_clip.asPath(animatedLocalTransform, targetBoundingBox, clipPath)) { AffineTransform positionTransform; positionTransform.translate(layerPositionOffset.x(), layerPositionOffset.y()); clipPath.transform(positionTransform); clipperState = ClipperState::AppliedPath; context.getPaintController().createAndAppend<BeginClipPathDisplayItem>( target, clipPath); return true; } // Fall back to masking. clipperState = ClipperState::AppliedMask; // Begin compositing the clip mask. CompositingRecorder::beginCompositing( context, target, SkXfermode::kSrcOver_Mode, 1, &paintInvalidationRect); { if (!drawClipAsMask(context, target, targetBoundingBox, paintInvalidationRect, animatedLocalTransform, layerPositionOffset)) { // End the clip mask's compositor. CompositingRecorder::endCompositing(context, target); return false; } } // Masked content layer start. CompositingRecorder::beginCompositing( context, target, SkXfermode::kSrcIn_Mode, 1, &paintInvalidationRect); return true; }
FilterEffect* FilterEffect::inputEffect(unsigned number) const { SECURITY_DCHECK(number < m_inputEffects.size()); return m_inputEffects.at(number).get(); }
void WEBPImageDecoder::applyPostProcessing(size_t frameIndex) { ImageFrame& buffer = m_frameBufferCache[frameIndex]; int width; int decodedHeight; if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0)) return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 if (decodedHeight <= 0) return; const IntRect& frameRect = buffer.originalFrameRect(); SECURITY_DCHECK(width == frameRect.width()); SECURITY_DCHECK(decodedHeight <= frameRect.height()); const int left = frameRect.x(); const int top = frameRect.y(); // TODO (msarett): // Here we apply the color space transformation to the dst space. // It does not really make sense to transform to a gamma-encoded // space and then immediately after, perform a linear premultiply // and linear blending. Can we find a way to perform the // premultiplication and blending in a linear space? SkColorSpaceXform* xform = colorTransform(); if (xform) { const SkColorSpaceXform::ColorFormat srcFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat; const SkColorSpaceXform::ColorFormat dstFormat = SkColorSpaceXform::kRGBA_8888_ColorFormat; for (int y = m_decodedHeight; y < decodedHeight; ++y) { const int canvasY = top + y; uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(left, canvasY)); xform->apply(dstFormat, row, srcFormat, row, width, kUnpremul_SkAlphaType); uint8_t* pixel = row; for (int x = 0; x < width; ++x, pixel += 4) { const int canvasX = left + x; buffer.setRGBA(canvasX, canvasY, pixel[0], pixel[1], pixel[2], pixel[3]); } } } // During the decoding of the current frame, we may have set some pixels to be // transparent (i.e. alpha < 255). If the alpha blend source was // 'BlendAtopPreviousFrame', the values of these pixels should be determined // by blending them against the pixels of the corresponding previous frame. // Compute the correct opaque values now. // FIXME: This could be avoided if libwebp decoder had an API that used the // previous required frame to do the alpha-blending by itself. if ((m_formatFlags & ANIMATION_FLAG) && frameIndex && buffer.getAlphaBlendSource() == ImageFrame::BlendAtopPreviousFrame && buffer.requiredPreviousFrameIndex() != kNotFound) { ImageFrame& prevBuffer = m_frameBufferCache[frameIndex - 1]; ASSERT(prevBuffer.getStatus() == ImageFrame::FrameComplete); ImageFrame::DisposalMethod prevDisposalMethod = prevBuffer.getDisposalMethod(); if (prevDisposalMethod == ImageFrame::DisposeKeep) { // Blend transparent pixels with pixels in previous canvas. for (int y = m_decodedHeight; y < decodedHeight; ++y) { m_blendFunction(buffer, prevBuffer, top + y, left, width); } } else if (prevDisposalMethod == ImageFrame::DisposeOverwriteBgcolor) { const IntRect& prevRect = prevBuffer.originalFrameRect(); // We need to blend a transparent pixel with the starting value (from just // after the initFrame() call). If the pixel belongs to prevRect, the // starting value was fully transparent, so this is a no-op. Otherwise, we // need to blend against the pixel from the previous canvas. for (int y = m_decodedHeight; y < decodedHeight; ++y) { int canvasY = top + y; int left1, width1, left2, width2; findBlendRangeAtRow(frameRect, prevRect, canvasY, left1, width1, left2, width2); if (width1 > 0) m_blendFunction(buffer, prevBuffer, canvasY, left1, width1); if (width2 > 0) m_blendFunction(buffer, prevBuffer, canvasY, left2, width2); } } } m_decodedHeight = decodedHeight; buffer.setPixelsChanged(true); }