void StackLayout::doRelayout()
{
	const List<DisplayItem *> &items = getItems();
	DisplayItem *inputField = items.getAt(0);
	int bottom;
	if (isItemVisible(inputField)) {
		setItemSize(inputField, getWidth(), font->getLineHeight());
		bottom = getHeight() - inputField->getHeight();
		setItemPosition(inputField, 0, bottom);
	}
	else {
		bottom = getHeight();
	}
	int i = 0;
	upperItemNr = 0;
	while (bottom > 0 && i + firstLevelVisible < stack->getLength()) {
		upperItemNr++;
		DisplayItem *item;
		if (items.getLength() <= upperItemNr) {
			item = stack->getAt(i + firstLevelVisible)->getDisplayItem(*font);
			addItem(item);
		}
		else {
			item = items.getAt(upperItemNr);
			if (item == NULL) {
				item = stack->getAt(i + firstLevelVisible)->getDisplayItem(*font);
				setItemAt(upperItemNr, item);
			}
		}
		if (item) {
			int nw = getNumberWidth(upperItemNr + firstLevelVisible);
			setItemSize(item, getWidth() - nw, -1);
			int h = item->getHeight();
			if (h < font->getLineHeight())
				h = font->getLineHeight();
			bottom -= h;
			int w = item->getWidth();
			if (w < getWidth() - nw)
				setItemPosition(item, getWidth() - w, bottom);
			else
				setItemPosition(item, nw, bottom);
		}
		else {
			bottom -= font->getLineHeight();
		}
		i++;
	}
	while (items.getLength() > upperItemNr + 1) {
		removeItemAt(upperItemNr + 1);
	}
	while (bottom > 0) {
		upperItemNr++;
		bottom -= font->getLineHeight();
	}
	upperItemScanStart = -bottom;
}
void DisplayItemList::addItemToIndexIfNeeded(const DisplayItem& displayItem, size_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient)
{
    if (!displayItem.isCacheable())
        return;

    DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find(displayItem.client());
    Vector<size_t>& indices = it == displayItemIndicesByClient.end() ?
        displayItemIndicesByClient.add(displayItem.client(), Vector<size_t>()).storedValue->value : it->value;
    indices.append(index);
}
int StackLayout::getItemHeight(int i)
{
	if (i >= getItems().getLength())
		return font->getLineHeight();
	int h = 0;
	DisplayItem *item = getItems().getAt(i);
	if (item)
		h = item->getHeight();
	if (h < font->getLineHeight())
		h = font->getLineHeight();
	return h;
}
static void showUnderInvalidationError(const char* reason, const DisplayItem& displayItem)
{
#ifndef NDEBUG
    WTFLogAlways("%s: %s\nSee http://crbug.com/450725.", reason, displayItem.asDebugString().utf8().data());
#else
    WTFLogAlways("%s. Run debug build to get more details\nSee http://crbug.com/450725.", reason);
#endif // NDEBUG
}
Exemple #5
0
bool PaintChunker::incrementDisplayItemIndex(const DisplayItem& item) {
  DCHECK(RuntimeEnabledFeatures::slimmingPaintV2Enabled());

  ItemBehavior behavior;
  Optional<PaintChunk::Id> newChunkId;
  if (DisplayItem::isForeignLayerType(item.getType())) {
    behavior = RequiresSeparateChunk;
    // Use null chunkId if we are skipping cache, so that the chunk will not
    // match any old chunk and will be treated as brand new.
    if (!item.skippedCache())
      newChunkId.emplace(item.getId());

    // Clear m_currentChunkId so that any display items after the foreign layer
    // without a new chunk id will be treated as having no id to avoid the chunk
    // from using the same id as the chunk before the foreign layer chunk.
    m_currentChunkId = WTF::nullopt;
  } else {
    behavior = DefaultBehavior;
    if (!item.skippedCache() && m_currentChunkId)
      newChunkId.emplace(*m_currentChunkId);
  }

  if (m_chunks.isEmpty()) {
    PaintChunk newChunk(0, 1, newChunkId ? &*newChunkId : nullptr,
                        m_currentProperties);
    m_chunks.append(newChunk);
    m_chunkBehavior.append(behavior);
    return true;
  }

  auto& lastChunk = m_chunks.last();
  bool canContinueChunk = m_currentProperties == lastChunk.properties &&
                          behavior != RequiresSeparateChunk &&
                          m_chunkBehavior.last() != RequiresSeparateChunk;
  if (canContinueChunk) {
    lastChunk.endIndex++;
    return false;
  }

  PaintChunk newChunk(lastChunk.endIndex, lastChunk.endIndex + 1,
                      newChunkId ? &*newChunkId : nullptr, m_currentProperties);
  m_chunks.append(newChunk);
  m_chunkBehavior.append(behavior);
  return true;
}
void PaintController::checkCachedDisplayItemIsUnchanged(const char* messagePrefix, const DisplayItem& newItem, const DisplayItem& oldItem)
{
    ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled());
    ASSERT(!newItem.isCached());
    ASSERT(!oldItem.isCached());

    if (newItem.skippedCache()) {
        showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: skipped-cache in cached subsequence", &newItem, &oldItem);
        ASSERT_NOT_REACHED();
    }

    if (newItem.isCacheable() && !m_validlyCachedClients.contains(&newItem.client())) {
        showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: invalidated in cached subsequence", &newItem, &oldItem);
        ASSERT_NOT_REACHED();
    }

    if (newItem.equals(oldItem))
        return;

    showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: display item changed", &newItem, &oldItem);

#ifndef NDEBUG
    if (newItem.isDrawing()) {
        RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(newItem).picture();
        RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(oldItem).picture();
        String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None";
        String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None";
        WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data());
        WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data());
    }
#endif // NDEBUG

    ASSERT_NOT_REACHED();
}
void StackLayout::initLineScanning(int scanLine, int drawOffsetX, int drawWidth)
{
	itemNr = upperItemNr;
	itemScanLine = upperItemScanStart + scanLine;
	while (itemScanLine > getItemHeight(itemNr)) {
		itemScanLine -= getItemHeight(itemNr);
		itemNr--;
	}
	this->drawOffsetX = drawOffsetX;
	this->drawWidth = drawWidth;
	currentNumberWidth = getNumberWidth(itemNr + firstLevelVisible);
	DisplayItem *item = NULL;
	if (itemNr < getItems().getLength())
		item = getItems().getAt(itemNr);
	if (item) {
		if (drawOffsetX <= item->getX())
			item->initLineScanning(scanLine - item->getY(), 0, drawWidth - item->getX());
		else
			item->initLineScanning(scanLine - item->getY(), drawOffsetX - item->getX(), drawWidth);
	}
}
void PaintController::processNewItem(DisplayItem& displayItem)
{
    ASSERT(!m_constructionDisabled);
    ASSERT(!skippingCache() || !displayItem.isCached());

    if (displayItem.isCached())
        ++m_numCachedNewItems;

#if ENABLE(ASSERT)
    // Verify noop begin/end pairs have been removed.
    if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) {
        const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList.size() - 2];
        if (beginDisplayItem.isBegin() && beginDisplayItem.type() != DisplayItem::Subsequence && !beginDisplayItem.drawsContent())
            ASSERT(!displayItem.isEndAndPairedWith(beginDisplayItem.type()));
    }
#endif

    if (!m_scopeStack.isEmpty())
        displayItem.setScope(m_scopeStack.last());

#if ENABLE(ASSERT)
    size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDisplayItemIndicesByClient, m_newDisplayItemList);
    if (index != kNotFound) {
#ifndef NDEBUG
        showDebugData();
        WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%d)\n",
            displayItem.asDebugString().utf8().data(), m_newDisplayItemList[index].asDebugString().utf8().data(), static_cast<int>(index));
#endif
        ASSERT_NOT_REACHED();
    }
    addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDisplayItemIndicesByClient);
#endif // ENABLE(ASSERT)

    if (skippingCache())
        displayItem.setSkippedCache();

    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
        m_newPaintChunks.incrementDisplayItemIndex();
}
void DisplayItemList::checkCachedDisplayItemIsUnchanged(const DisplayItem& displayItem, DisplayItemIndicesByClientMap& displayItemIndicesByClient)
{
    ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled());

    if (!displayItem.isDrawing() || displayItem.skippedCache() || !clientCacheIsValid(displayItem.client()))
        return;

    // If checking under-invalidation, we always generate new display item even if the client is not invalidated.
    // Checks if the new picture is the same as the cached old picture. If the new picture is different but
    // the client is not invalidated, issue error about under-invalidation.
    size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), displayItemIndicesByClient, m_currentDisplayItems);
    if (index == kNotFound) {
        showUnderInvalidationError("ERROR: under-invalidation: no cached display item", displayItem);
        ASSERT_NOT_REACHED();
        return;
    }

    DisplayItems::iterator foundItem = m_currentDisplayItems.begin() + index;
    RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(displayItem).picture();
    RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(*foundItem).picture();
    // Invalidate the display item so that we can check if there are any remaining cached display items after merging.
    foundItem->clearClientForUnderInvalidationChecking();

    if (!newPicture && !oldPicture)
        return;
    if (newPicture && oldPicture) {
        switch (static_cast<const DrawingDisplayItem&>(displayItem).underInvalidationCheckingMode()) {
        case DrawingDisplayItem::CheckPicture:
            if (newPicture->approximateOpCount() == oldPicture->approximateOpCount()) {
                SkDynamicMemoryWStream newPictureSerialized;
                newPicture->serialize(&newPictureSerialized);
                SkDynamicMemoryWStream oldPictureSerialized;
                oldPicture->serialize(&oldPictureSerialized);

                if (newPictureSerialized.bytesWritten() == oldPictureSerialized.bytesWritten()) {
                    RefPtr<SkData> oldData = adoptRef(oldPictureSerialized.copyToData());
                    RefPtr<SkData> newData = adoptRef(newPictureSerialized.copyToData());
                    if (oldData->equals(newData.get()))
                        return;
                }
            }
            break;
        case DrawingDisplayItem::CheckBitmap:
            if (newPicture->cullRect() == oldPicture->cullRect()) {
                SkBitmap bitmap;
                SkRect rect = newPicture->cullRect();
                bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height()));
                SkCanvas canvas(bitmap);
                canvas.translate(-rect.x(), -rect.y());
                canvas.drawPicture(oldPicture.get());
                SkPaint diffPaint;
                diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode);
                canvas.drawPicture(newPicture.get(), nullptr, &diffPaint);
                if (bitmapIsAllZero(bitmap)) // Contents are the same.
                    return;
            }
        default:
            ASSERT_NOT_REACHED();
        }
    }

    showUnderInvalidationError("ERROR: under-invalidation: display item changed", displayItem);
#ifndef NDEBUG
    String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None";
    String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None";
    WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data());
    WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data());
#endif // NDEBUG

    ASSERT_NOT_REACHED();
}
void StackLayout::processLine(uint8_t *pixelData, int dataOffset)
{
	if (itemNr < 0)
		return;
	if (itemScanLine >= getItemHeight(itemNr)) {
		itemScanLine = 0;
		if (--itemNr < 0)
			return;
		currentNumberWidth = getNumberWidth(itemNr + firstLevelVisible);
	}
	DisplayItem *item = NULL;
	if (itemNr < getItems().getLength())
		item = getItems().getAt(itemNr);
	if (itemScanLine == 0 && item) {
		if (drawOffsetX <= item->getX())
			item->initLineScanning(0, 0, drawWidth - item->getX());
		else
			item->initLineScanning(0, drawOffsetX - item->getX(), drawWidth);
	}

	if (itemNr > 0)
		drawNumber(pixelData, dataOffset, itemNr + firstLevelVisible);
	if (item != NULL && isItemVisible(item)) {
		if (itemScanLine < item->getHeight()) {
			if (drawOffsetX <= item->getX())
				item->processLine(pixelData, dataOffset + item->getX() - drawOffsetX);
			else
				item->processLine(pixelData, dataOffset);
		}
	}

	itemScanLine++;
}