Exemplo n.º 1
0
// 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();
  }
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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));
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
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];
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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();
}
Exemplo n.º 11
0
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()];
}
Exemplo n.º 12
0
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;
}
Exemplo n.º 13
0
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();
}
Exemplo n.º 14
0
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);
}
Exemplo n.º 15
0
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());
  }
}
Exemplo n.º 16
0
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);
}
Exemplo n.º 17
0
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();
}
Exemplo n.º 18
0
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;
}
Exemplo n.º 19
0
FilterEffect* FilterEffect::inputEffect(unsigned number) const {
  SECURITY_DCHECK(number < m_inputEffects.size());
  return m_inputEffects.at(number).get();
}
Exemplo n.º 20
0
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);
}