size_t WaitableEvent::waitMultiple(const WTF::Vector<WaitableEvent*>& events)
{
    std::vector<base::WaitableEvent*> baseEvents;
    for (size_t i = 0; i < events.size(); ++i)
        baseEvents.push_back(events[i]->m_impl.get());
    size_t idx = base::WaitableEvent::WaitMany(baseEvents.data(), baseEvents.size());
    DCHECK_LT(idx, events.size());
    return idx;
}
示例#2
0
void MediaConstraints::getOptionalConstraints(std::vector<MediaConstraint>& constraints) const
{
    ASSERT(!isNull());
    WTF::Vector<WebCore::MediaConstraint> optionalConstraints;
    m_private->getOptionalConstraints(optionalConstraints);
    std::vector<MediaConstraint> result(optionalConstraints.size());
    for (size_t i = 0; i < optionalConstraints.size(); ++i)
        result[i] = toNixMediaConstraint(optionalConstraints[i]);
    constraints.swap(result);
}
示例#3
0
jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
    if (!item)
        return NULL;

    // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
    v.reserveCapacity(HISTORY_MIN_SIZE);

    // Write the top-level history item and then write all the children
    // recursively.
    LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
    write_item(v, item);
    write_children_recursive(v, item);

//SAMSUNG - CRASH FIX BEGIN

    int size = v.size();
    if (size > 0) 
    {
        long availableMemory = GetVmAvailableMemory(env);

        if(size > availableMemory)
        {
            LOGV("WebHistory::Flatten(): load size=%d, availableMemory=%ld, still larger, return NULL", size, availableMemory);		
            return NULL;
        }

        checkException(env);
    }

//SAMSUNG - CRASH FIX END
    // Try to create a new java byte array.
    jbyteArray b = env->NewByteArray(v.size());
    if (!b)
    {
//SAMSUNG - CRASH FIX BEGIN
        if (checkException(env))
        {
            LOGV("WebHistory::Flatten(): env exception happened while allocating %d bytes, clear pending exception", v.size());
            env->ExceptionClear();
        }
//SAMSUNG - CRASH FIX END
        return NULL;
    }

    // Write our flattened data to the java array.
    env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
    return b;
}
示例#4
0
void SpellCheckerClientImpl::checkGrammarOfString(const String& text, WTF::Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
{
    if (badGrammarLocation)
        *badGrammarLocation = -1;
    if (badGrammarLength)
        *badGrammarLength = 0;

    if (!m_webView->spellCheckClient())
        return;
    WebVector<WebTextCheckingResult> webResults;
    m_webView->spellCheckClient()->checkTextOfParagraph(text, WebTextCheckingTypeGrammar, &webResults);
    if (!webResults.size())
        return;

    // Convert a list of WebTextCheckingResults to a list of GrammarDetails. If
    // the converted vector of GrammarDetails has grammar errors, we set
    // badGrammarLocation and badGrammarLength to tell WebKit that the input
    // text has grammar errors.
    for (size_t i = 0; i < webResults.size(); ++i) {
        if (webResults[i].decoration == WebTextDecorationTypeGrammar) {
            GrammarDetail detail;
            detail.location = webResults[i].location;
            detail.length = webResults[i].length;
            detail.userDescription = webResults[i].replacement;
            details.append(detail);
        }
    }
    if (!details.size())
        return;
    if (badGrammarLocation)
        *badGrammarLocation = 0;
    if (badGrammarLength)
        *badGrammarLength = text.length();
}
示例#5
0
static void write_string(WTF::Vector<char>& v, const WebCore::String& str)
{
    unsigned strLen = str.length();
    // Only do work if the string has data.
    if (strLen) {
        // Determine how much to grow the vector. Use the worst case for utf8 to
        // avoid reading the string twice. Add sizeof(unsigned) to hold the
        // string length in utf8.
        unsigned vectorLen = v.size() + sizeof(unsigned);
        unsigned length = (strLen << 2) + vectorLen;
        // Grow the vector. This will change the value of v.size() but we
        // remember the original size above.
        v.grow(length);
        // Grab the position to write to.
        char* data = v.begin() + vectorLen;
        // Write the actual string
        int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
        LOGV("Writing string       %d %.*s", l, l, data);
        // Go back and write the utf8 length. Subtract sizeof(unsigned) from
        // data to get the position to write the length.
        memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned));
        // Shrink the internal state of the vector so we match what was
        // actually written.
        v.shrink(vectorLen + l);
    } else
        v.append((char*)&strLen, sizeof(unsigned));
}
void GLExtras::drawFindOnPage(SkRect& viewport)
{
    WTF::Vector<MatchInfo>* matches = m_findOnPage->matches();
    XLOG("drawFindOnPage, matches: %p", matches);
    if (!matches || !m_findOnPage->isCurrentLocationValid())
        return;
    int count = matches->size();
    int current = m_findOnPage->currentMatchIndex();
    XLOG("match count: %d", count);
    if (count < MAX_NUMBER_OF_MATCHES_TO_DRAW)
        for (int i = 0; i < count; i++) {
            MatchInfo& info = matches->at(i);
            const SkRegion& region = info.getLocation();
            SkIRect rect = region.getBounds();
            if (rect.intersect(viewport.fLeft, viewport.fTop,
                               viewport.fRight, viewport.fBottom))
                drawRegion(region, i == current, false, true);
#ifdef DEBUG
            else
                XLOG("Quick rejecting [%dx%d, %d, %d", rect.fLeft, rect.fTop,
                     rect.width(), rect.height());
#endif // DEBUG
        }
    else {
        MatchInfo& info = matches->at(current);
        drawRegion(info.getLocation(), true, false, true);
    }
}
void TilesManager::dirtyTexturesVector(WTF::Vector<TileTexture*>& textures)
{
    for (unsigned int i = 0; i < textures.size(); i++) {
        Tile* currentOwner = static_cast<Tile*>(textures[i]->owner());
        if (currentOwner)
            currentOwner->markAsDirty();
    }
}
示例#8
0
void PepperPluginImpl::updateGeometry(const WebCore::IntRect& window_rect, const WebCore::IntRect& clip_rect, const WTF::Vector<WebCore::IntRect>& cut_outs_rects, bool is_visible){
  plugin_rect_ = window_rect;
  if (!instance_->FlashIsFullscreenOrPending()) {
    std::vector<IntRect> cut_outs;
    for (size_t i = 0; i < cut_outs_rects.size(); ++i)
      cut_outs.push_back(cut_outs_rects[i]);
    instance_->ViewChanged(plugin_rect_, clip_rect, cut_outs);
  }
}
void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount,
                                         WTF::Vector<TileTexture*>& textures,
                                         bool deallocateGLTextures)
{
    const unsigned int max = textures.size();
    int dealloc = 0;
    WTF::Vector<int> discardedIndex;
    for (unsigned int i = 0; i < max; i++) {
        TextureOwner* owner = textures[i]->owner();
        if (!owner || owner->drawCount() < sparedDrawCount) {
            if (deallocateGLTextures) {
                // deallocate textures' gl memory
                textures[i]->discardGLTexture();
                discardedIndex.append(i);
            } else if (owner) {
                // simply detach textures from owner
                static_cast<Tile*>(owner)->discardTextures();
            }
            dealloc++;
        }
    }

    bool base = textures == m_textures;
    // Clean up the vector of TileTextures and reset the max texture count.
    if (discardedIndex.size()) {
        android::Mutex::Autolock lock(m_texturesLock);
        for (int i = discardedIndex.size() - 1; i >= 0; i--)
            textures.remove(discardedIndex[i]);

        int remainedTextureNumber = textures.size();
        int* countPtr = base ? &m_currentTextureCount : &m_currentLayerTextureCount;
        if (remainedTextureNumber < *countPtr) {
            ALOGV("reset currentTextureCount for %s tiles from %d to %d",
                  base ? "base" : "layer", *countPtr, remainedTextureNumber);
            *countPtr = remainedTextureNumber;
        }

    }

    ALOGV("Discarded %d %s textures (out of %d %s tiles)",
          dealloc, (deallocateGLTextures ? "gl" : ""),
          max, base ? "base" : "layer");
}
示例#10
0
void MaskNode::OnSelfOrChildStateChange() {
	m_bChildNeedDraw = true;
	m_bBoundingDirty = true; // 不需要调用SetBoundingDirty(),因为不需要通知父节点

	WTF::Vector<UINode*>* owners = m_pManager->GetEffectsResOwnerNodes(this);
	if (!owners)
		return;

	// 子节点绘图消息被转发到拥有者节点上
	for (size_t i = 0; i < owners->size(); ++i)
		owners->at(i)->SetNeedLayout();
}
    void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
    {
        // Anti-aliased clipping:
        //
        // Refer to PlatformContextSkia.cpp's applyAntiAliasedClipPaths() for more details

        if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)
            m_platformGfxCtx->mCanvas->restore();

        SkPaint paint;
        paint.setXfermodeMode(SkXfermode::kClear_Mode);
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kFill_Style);

        if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)  {
            for (size_t i = paths.size() - 1; i < paths.size(); --i) {
                paths[i].setFillType(SkPath::kInverseWinding_FillType);
                m_platformGfxCtx->mCanvas->drawPath(paths[i], paint);
            }
            m_platformGfxCtx->mCanvas->restore();
        } else
            ASSERT(0);
    }
示例#12
0
jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
    if (!item)
        return NULL;

    // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
    v.reserveCapacity(HISTORY_MIN_SIZE);

    // Write the top-level history item and then write all the children
    // recursively.
    LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
    write_item(v, item);
    write_children_recursive(v, item);

    // Try to create a new java byte array.
    jbyteArray b = env->NewByteArray(v.size());
    if (!b)
        return NULL;

    // Write our flattened data to the java array.
    env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
    return b;
}
void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
{
    // Anti-aliased clipping:
    //
    // Skia's clipping is 1-bit only. Consider what would happen if it were 8-bit:
    // We have a square canvas, filled with white and we declare a circular
    // clipping path. Then we fill twice with a black rectangle. The fractional
    // pixels would first get the correct color (white * alpha + black * (1 -
    // alpha)), but the second fill would apply the alpha to the already
    // modified color and the result would be too dark.
    //
    // This, anti-aliased clipping needs to be performed after the drawing has
    // been done. In order to do this, we create a new layer of the canvas in
    // clipPathAntiAliased and store the clipping path. All drawing is done to
    // the layer's bitmap while it's in effect. When WebKit calls restore() to
    // undo the clipping, this function is called.
    //
    // Here, we walk the list of clipping paths backwards and, for each, we
    // clear outside of the clipping path. We only need a single extra layer
    // for any number of clipping paths.
    //
    // When we call restore on the SkCanvas, the layer's bitmap is composed
    // into the layer below and we end up with correct, anti-aliased clipping.

    SkPaint paint;
    paint.setXfermodeMode(SkXfermode::kClear_Mode);
    paint.setAntiAlias(true);
    paint.setStyle(SkPaint::kFill_Style);

    for (size_t i = paths.size() - 1; i < paths.size(); --i) {
        paths[i].setFillType(SkPath::kInverseWinding_FillType);
        m_canvas->drawPath(paths[i], paint);
    }

    m_canvas->restore();
}
void TilesManager::deallocateTexturesVector(unsigned long long sparedDrawCount,
                                            WTF::Vector<BaseTileTexture*>& textures)
{
    const unsigned int max = textures.size();
    int dealloc = 0;
    for (unsigned int i = 0; i < max; i++) {
        TextureOwner* owner = textures[i]->owner();
        if (!owner || owner->drawCount() < sparedDrawCount) {
            textures[i]->discardGLTexture();
            dealloc++;
        }
    }
    XLOG("Deallocated %d gl textures (out of %d base tiles and %d layer tiles)",
         dealloc, max, maxLayer);
}
// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
void ReverseBidi(UChar* chars, int len) {
    using namespace WTF::Unicode;
    WTF::Vector<UChar> result;
    result.reserveCapacity(len);
    TextRun run(chars, len);
    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
    bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
        BidiContext::create(0, LeftToRight, false)));
    bidiResolver.setPosition(TextRunIterator(&run, 0));
    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
    if (!bidiRuns.runCount())
        return;
    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
    while (bidiRun) {
        int bidiStart = bidiRun->start();
        int bidiStop = bidiRun->stop();
        int size = result.size();
        int bidiCount = bidiStop - bidiStart;
        result.append(chars + bidiStart, bidiCount);
        if (bidiRun->level() % 2) {
            UChar* start = &result[size];
            UChar* end = start + bidiCount;
            // reverse the order of any RTL substrings
            while (start < end) {
                UChar temp = *start;
                *start++ = *--end;
                *end = temp;
            }
            start = &result[size];
            end = start + bidiCount - 1;
            // if the RTL substring had a surrogate pair, restore its order
            while (start < end) {
                UChar trail = *start++;
                if (!U16_IS_SURROGATE(trail))
                    continue;
                start[-1] = *start; // lead
                *start++ = trail;
            }
        }
        bidiRun = bidiRun->next();
    }
    bidiRuns.deleteRuns();
    memcpy(chars, &result[0], len * sizeof(UChar));
}
void TilesManager::paintedSurfacesCleanup(GLWebViewState* state)
{
    // PaintedSurfaces are created by LayerAndroid with a refcount of 1,
    // and just transferred to new (corresponding) layers when a new layer tree
    // is received.
    // PaintedSurface also keep a reference on the Layer it currently has, so
    // when we unref the tree of layer, those layers with a PaintedSurface will
    // still be around if we do nothing.
    // Here, if the surface does not have any associated layer, it means that we
    // received a new layer tree without a corresponding layer (i.e. a layer
    // using a texture has been removed by webkit).
    // In that case, we remove the PaintedSurface from our list, and unref it.
    // If the surface does have a layer, but the GLWebViewState associated to
    // that layer is different from the one passed in parameter, it means we can
    // also remove the surface (and we also remove/unref any layer that surface
    // has). We do this when we deallocate GLWebViewState (i.e. the webview has
    // been destroyed) and also when we switch to a page without
    // composited layers.

    WTF::Vector<PaintedSurface*> collect;
    for (unsigned int i = 0; i < m_paintedSurfaces.size(); i++) {
        PaintedSurface* surface = m_paintedSurfaces[i];

        Layer* drawing = surface->drawingLayer();
        Layer* painting = surface->paintingLayer();

        XLOG("considering PS %p, drawing %p, painting %p", surface, drawing, painting);

        bool drawingMatchesState = state && drawing && (drawing->state() == state);
        bool paintingMatchesState = state && painting && (painting->state() == state);

        if ((!painting && !drawing) || drawingMatchesState || paintingMatchesState) {
            XLOG("trying to remove PS %p, painting %p, drawing %p, DMS %d, PMS %d",
                 surface, painting, drawing, drawingMatchesState, paintingMatchesState);
            collect.append(surface);
        }
    }
    for (unsigned int i = 0; i < collect.size(); i++) {
        PaintedSurface* surface = collect[i];
        m_paintedSurfaces.remove(m_paintedSurfaces.find(surface));
        SkSafeUnref(surface);
    }
}
void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) {
    ASSERT(m_frame);
    // Ignore ErrorInterrupted since it is due to a policy interruption. This
    // is caused by a decision to download the main resource rather than
    // display it.
    if (error.errorCode() == InternalErrorInterrupted
            || error.errorCode() == InternalErrorCancelled) {
        // If we decided to download the main resource or if the user cancelled
        // it, make sure we report that the load is done.
        didFinishLoad();
        return;
    }

    AssetManager* am = globalAssetManager();

    // Check to see if the error code was not generated internally
    WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain;
    if ((error.errorCode() == ErrorFile ||
            error.errorCode() == ErrorFileNotFound) &&
            (!error.localizedDescription().isEmpty())) {
        id = WebCore::PlatformBridge::LoadError;
    }
    String filename = m_webFrame->getRawResourceFilename(id);
    if (filename.isEmpty())
        return;

    // Grab the error page from the asset manager
    Asset* a = am->openNonAsset(
            filename.utf8().data(), Asset::ACCESS_BUFFER);
    if (!a)
        return;

    // Take the failing url and encode html entities so javascript urls are not
    // executed.
    CString failingUrl = error.failingURL().utf8();
    WTF::Vector<char> url;
    int len = failingUrl.length();
    const char* data = failingUrl.data();
    for (int i = 0; i < len; i++) {
        char c = data[i];
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                || (c >= '0' && c <= '9'))
            url.append(c);
        else {
            char buf[16];
            int res = sprintf(buf, "&#%d;", c);
            buf[res] = 0;
            url.append(buf, res);
        }
    }


    // Replace all occurances of %s with the failing url.
    String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength());

	// samsung shkim 
	// \frameworks\base\core\res\res\raw-XX\nodomain.html or loaderror.html
	// These error pages does not have <viewport> tag, it is loaded as low zoom scale
	if( s.contains( "viewport" ) == false )
		s = s.replace( "<head>", "<head> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"/>" );
	
    s = s.replace("%s", String(url.data(), url.size()));

    // Replace all occurances of %e with the error text
    s = s.replace("%e", error.localizedDescription());

    // Create the request and the substitute data and tell the FrameLoader to
    // load with the replacement data.
    // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for
    // invalidate URL string.
    loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s);

    // Delete the asset.
    delete a;
}
示例#18
0
Value RelationOp::doEvaluate() const
{
    Value lhs( subExpr( 0 )->evaluate() );
    Value rhs( subExpr( 1 )->evaluate() );

    if (lhs.isNodeset() || rhs.isNodeset())
    {
        // If both are nodesets, or one is a string our
        // comparisons are based on strings.
        if ((lhs.isNodeset() && rhs.isNodeset()) ||
                (lhs.isString()  || rhs.isString())) {

            WTF::Vector<DOM::DOMString> leftStrings;
            WTF::Vector<DOM::DOMString> rightStrings;

            stringify(lhs, &leftStrings);
            stringify(rhs, &rightStrings);

            for (unsigned pl = 0; pl < leftStrings.size(); ++pl) {
                for (unsigned pr = 0; pr < rightStrings.size(); ++pr) {
                    if (compareStrings(leftStrings[pl], rightStrings[pr]))
                        return Value(true);
                } // pr
            } // pl
            return Value(false);
        }

        // If one is a number, we do a number-based comparison
        if (lhs.isNumber() || rhs.isNumber()) {
            WTF::Vector<double> leftNums;
            WTF::Vector<double> rightNums;

            numify(lhs, &leftNums);
            numify(rhs, &rightNums);

            for (unsigned pl = 0; pl < leftNums.size(); ++pl) {
                for (unsigned pr = 0; pr < rightNums.size(); ++pr) {
                    if (compareNumbers(leftNums[pl], rightNums[pr]))
                        return Value(true);
                } // pr
            } // pl
            return Value(false);
        }

        // Has to be a boolean-based comparison.
        // These ones are simpler, since we just convert the nodeset to a bool
        assert(lhs.isBoolean() || rhs.isBoolean());

        if (lhs.isNodeset())
            lhs = Value(lhs.toBoolean());
        else
            rhs = Value(rhs.toBoolean());
    } // nodeset comparisons


    if (opCode == OP_EQ || opCode == OP_NE) {
        bool equal;
        if ( lhs.isBoolean() || rhs.isBoolean() ) {
            equal = ( lhs.toBoolean() == rhs.toBoolean() );
        } else if ( lhs.isNumber() || rhs.isNumber() ) {
            equal = ( lhs.toNumber() == rhs.toNumber() );
        } else {
            equal = ( lhs.toString() == rhs.toString() );
        }

        if ( opCode == OP_EQ )
            return Value( equal );
        else
            return Value( !equal );

    }

    // For other ops, we always convert to numbers
    double leftVal = lhs.toNumber(), rightVal = rhs.toNumber();
    return Value(compareNumbers(leftVal, rightVal));
}
TileTexture* TilesManager::getAvailableTexture(Tile* owner)
{
    android::Mutex::Autolock lock(m_texturesLock);

    WTF::Vector<TileTexture*>* availableTexturePool;
    if (owner->isLayerTile())
        availableTexturePool = &m_availableTilesTextures;
    else
        availableTexturePool = &m_availableTextures;

    // Sanity check that the tile does not already own a texture
    if (owner->backTexture() && owner->backTexture()->owner() == owner) {
        int removeIndex = availableTexturePool->find(owner->backTexture());

        // TODO: investigate why texture isn't found
        if (removeIndex >= 0)
            availableTexturePool->remove(removeIndex);
        return owner->backTexture();
    }

    // The heuristic for selecting a texture is as follows:
    //  1. Skip textures currently being painted, they can't be painted while
    //         busy anyway
    //  2. If a tile isn't owned, break with that one
    //  3. Don't let tiles acquire their front textures
    //  4. Otherwise, use the least recently prepared tile, but ignoring tiles
    //         drawn in the last frame to avoid flickering

    TileTexture* farthestTexture = 0;
    unsigned long long oldestDrawCount = getDrawGLCount() - 1;
    const unsigned int max = availableTexturePool->size();
    for (unsigned int i = 0; i < max; i++) {
        TileTexture* texture = (*availableTexturePool)[i];
        Tile* currentOwner = static_cast<Tile*>(texture->owner());
        if (!currentOwner) {
            // unused texture! take it!
            farthestTexture = texture;
            break;
        }

        if (currentOwner == owner) {
            // Don't let a tile acquire its own front texture, as the
            // acquisition logic doesn't handle that
            continue;
        }

        unsigned long long textureDrawCount = currentOwner->drawCount();
        if (oldestDrawCount > textureDrawCount) {
            farthestTexture = texture;
            oldestDrawCount = textureDrawCount;
        }
    }

    if (farthestTexture) {
        Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner());
        if (farthestTexture->acquire(owner)) {
            if (previousOwner) {
                previousOwner->removeTexture(farthestTexture);

                ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)",
                      owner->isLayerTile() ? "LAYER" : "BASE",
                      farthestTexture, previousOwner->x(), previousOwner->y(),
                      owner->x(), owner->y(),
                      oldestDrawCount, getDrawGLCount());
            }

            availableTexturePool->remove(availableTexturePool->find(farthestTexture));
            return farthestTexture;
        }
    } else {
        if (owner->isLayerTile()) {
            // couldn't find a tile for a layer, layers shouldn't request redraw
            // TODO: once we do layer prefetching, don't set this for those
            // tiles
            m_layerTexturesRemain = false;
        }
    }

    ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available",
          owner->isLayerTile() ? "LAYER" : "BASE",
          owner, owner->x(), owner->y(), max);
#ifdef DEBUG
    printTextures();
#endif // DEBUG
    return 0;
}
BaseTileTexture* TilesManager::getAvailableTexture(BaseTile* owner)
{
    android::Mutex::Autolock lock(m_texturesLock);

    // Sanity check that the tile does not already own a texture
    if (owner->backTexture() && owner->backTexture()->owner() == owner) {
        XLOG("same owner (%d, %d), getAvailableBackTexture(%x) => texture %x",
             owner->x(), owner->y(), owner, owner->backTexture());
        if (owner->isLayerTile())
            m_availableTilesTextures.remove(m_availableTilesTextures.find(owner->backTexture()));
        else
            m_availableTextures.remove(m_availableTextures.find(owner->backTexture()));
        return owner->backTexture();
    }

    WTF::Vector<BaseTileTexture*>* availableTexturePool;
    if (owner->isLayerTile()) {
        availableTexturePool = &m_availableTilesTextures;
    } else {
        availableTexturePool = &m_availableTextures;
    }

    // The heuristic for selecting a texture is as follows:
    //  1. Skip textures currently being painted, they can't be painted while
    //         busy anyway
    //  2. If a tile isn't owned, break with that one
    //  3. Don't let tiles acquire their front textures
    //  4. If we find a tile in the same page with a different scale,
    //         it's old and not visible. Break with that one
    //  5. Otherwise, use the least recently prepared tile, but ignoring tiles
    //         drawn in the last frame to avoid flickering

    BaseTileTexture* farthestTexture = 0;
    unsigned long long oldestDrawCount = getDrawGLCount() - 1;
    const unsigned int max = availableTexturePool->size();
    for (unsigned int i = 0; i < max; i++) {
        BaseTileTexture* texture = (*availableTexturePool)[i];
        BaseTile* currentOwner = static_cast<BaseTile*>(texture->owner());

        if (texture->busy()) {
            // don't bother, since the acquire() will likely fail
            continue;
        }

        if (!currentOwner) {
            // unused texture! take it!
            farthestTexture = texture;
            break;
        }

        if (currentOwner == owner) {
            // Don't let a tile acquire its own front texture, as the
            // acquisition logic doesn't handle that
            continue;
        }

        if (currentOwner->painter() == owner->painter() && texture->scale() != owner->scale()) {
            // if we render the back page with one scale, then another while
            // still zooming, we recycle the tiles with the old scale instead of
            // taking ones from the front page
            farthestTexture = texture;
            break;
        }

        unsigned long long textureDrawCount = currentOwner->drawCount();
        if (oldestDrawCount > textureDrawCount) {
            farthestTexture = texture;
            oldestDrawCount = textureDrawCount;
        }
    }

    if (farthestTexture) {
        BaseTile* previousOwner = static_cast<BaseTile*>(farthestTexture->owner());
        if (farthestTexture->acquire(owner)) {
            if (previousOwner) {
                previousOwner->removeTexture(farthestTexture);

                XLOG("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)",
                     owner->isLayerTile() ? "LAYER" : "BASE",
                     farthestTexture, previousOwner->x(), previousOwner->y(),
                     owner->x(), owner->y(),
                     oldestDrawCount, getDrawGLCount());
            }

            availableTexturePool->remove(availableTexturePool->find(farthestTexture));
            return farthestTexture;
        }
    } else {
        if (owner->isLayerTile()) {
            // couldn't find a tile for a layer, layers shouldn't request redraw
            // TODO: once we do layer prefetching, don't set this for those
            // tiles
            m_layerTexturesRemain = false;
        }
    }

    XLOG("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available",
          owner->isLayerTile() ? "LAYER" : "BASE",
          owner, owner->x(), owner->y(), max);
#ifdef DEBUG
    printTextures();
#endif // DEBUG
    return 0;
}