, m_deferSetNeedsLayouts(0)
, m_setNeedsLayoutWasDeferred(false)
, m_scrollCorner(0)
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ , m_hasOverflowScroll(false)
+#endif
{
init();
}
RenderView* view = m_frame->contentRenderer();
if (!view)
return;
-#if ENABLE(ANDROID_OVERFLOW_SCROLL)
- // Enter compositing mode for child frames that have layout dimensions.
- if (hasOverflowScroll())
- enterCompositingMode();
-#endif
// This call will make sure the cached hasAcceleratedCompositing is updated from the pref
view->compositor()->cacheAcceleratedCompositingFlags();
page->chrome()->client()->setNeedsOneShotDrawingSynchronization();
}
-#if ENABLE(ANDROID_OVERFLOW_SCROLL)
-bool FrameView::hasOverflowScroll() const
-{
-#ifndef ANDROID_FLATTEN_IFRAME
- RenderObject* ownerRenderer = m_frame->ownerRenderer();
- if (!ownerRenderer || !ownerRenderer->isRenderIFrame())
- return false;
- RenderLayer* layer = ownerRenderer->enclosingLayer();
- // Make sure the layer has visible content or descendants.
- if (!layer->hasVisibleContent() && !layer->hasVisibleDescendant())
- return false;
- // If either layout dimension is 0, return false.
- if (!layoutWidth() || !layoutHeight())
- return false;
- ScrollbarMode h, v;
- scrollbarModes(h, v);
- if (h == ScrollbarAlwaysOff || v == ScrollbarAlwaysOff)
- return false;
- if (contentsWidth() <= layoutWidth() && contentsHeight() <= layoutHeight())
- return false;
- return true;
-#endif
- return false;
-}
-#endif
#endif // USE(ACCELERATED_COMPOSITING)
bool FrameView::hasCompositedContent() const
InspectorInstrumentation::didLayout(cookie);
m_nestedLayoutCount--;
+#if ENABLE(ANDROID_OVERFLOW_SCROLL) && !defined(ANDROID_FLATTEN_IFRAME)
+ // Reset to false each time we layout in case the overflow status changed.
+ bool hasOverflowScroll = false;
+ RenderObject* ownerRenderer = m_frame->ownerRenderer();
+ if (ownerRenderer && ownerRenderer->isRenderIFrame()) {
+ RenderLayer* layer = ownerRenderer->enclosingLayer();
+ if (layer) {
+ // Some sites use tiny iframes for loading so don't composite those.
+ if (canHaveScrollbars() && layoutWidth() > 1 && layoutHeight() > 1)
+ hasOverflowScroll = layoutWidth() < contentsWidth() || layoutHeight() < contentsHeight();
+ }
+ }
+ if (RenderView* view = m_frame->contentRenderer()) {
+ if (hasOverflowScroll != m_hasOverflowScroll) {
+ if (hasOverflowScroll)
+ enterCompositingMode();
+ else
+ // We are leaving overflow mode so we need to update the layer
+ // tree in case that is the reason we were composited.
+ view->compositor()->scheduleCompositingLayerUpdate();
+ }
+ }
+ m_hasOverflowScroll = hasOverflowScroll;
+#endif
}
void FrameView::addWidgetToUpdate(RenderEmbeddedObject* object)
// Called when changes to the GraphicsLayer hierarchy have to be synchronized with
// content rendered via the normal painting path.
void setNeedsOneShotDrawingSynchronization();
-#if ENABLE(ANDROID_OVERFLOW_SCROLL)
- bool hasOverflowScroll() const;
#endif
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ bool hasOverflowScroll() const { return m_hasOverflowScroll; }
#endif
bool hasCompositedContent() const;
static double s_initialDeferredRepaintDelayDuringLoading;
static double s_maxDeferredRepaintDelayDuringLoading;
static double s_deferredRepaintDelayIncrementDuringLoading;
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ bool m_hasOverflowScroll;
+#endif
};
} // namespace WebCore
m_busy = false;
}
+bool BackedDoubleBufferedTexture::busy()
+{
+ android::Mutex::Autolock lock(m_busyLock);
+ return m_busy;
+}
+
void BackedDoubleBufferedTexture::producerUpdate(TextureInfo* textureInfo)
{
// no need to upload a texture since the bitmap is empty
android::Mutex::Autolock lock(m_busyLock);
if (!m_busy) {
if (m_owner)
- m_owner->removeTexture();
+ m_owner->removeTexture(this);
m_owner = owner;
return true;
}
TextureOwner* owner() { return m_owner; } // only used by the consumer thread
SkCanvas* canvas() { return m_canvas; } // only used by the producer thread
- // This is to be only used for debugging on the producer thread
- bool busy() { return m_busy; }
+ bool busy();
const SkSize& getSize() const { return m_size; }
#ifdef DEBUG
TilesManager::instance()->printLayersTextures("reserve");
#endif
+ // Get the current scale; if we are zooming, we don't change the scale
+ // factor immediately (see BaseLayerAndroid::drawBasePictureInGL()), but
+ // we change the scaleRequestState. When the state is kReceivedNewScale
+ // we can use the future scale instead of the current scale to request
+ // new textures. After a transition time, the scaleRequestState will be
+ // reset and the current scale will be set to the future scale.
+ float scale = m_glWebViewState->currentScale();
+ if (m_glWebViewState->scaleRequestState() == GLWebViewState::kReceivedNewScale) {
+ scale = m_glWebViewState->futureScale();
+ }
+ compositedRoot->setScale(scale);
compositedRoot->reserveGLTextures();
- // Now that we marked the textures being used, we delete the unnecessary
- // ones to make space...
- TilesManager::instance()->cleanupLayersTextures();
- // Finally do another pass to create new textures if needed
+ // Now that we marked the textures being used, we delete
+ // the unnecessary ones to make space...
+ TilesManager::instance()->cleanupLayersTextures(compositedRoot);
+ // Finally do another pass to create new textures and schedule
+ // repaints if needed
compositedRoot->createGLTextures();
if (compositedRoot->drawGL(matrix))
m_texture = texture;
}
-void BaseTile::removeTexture()
+void BaseTile::removeTexture(BackedDoubleBufferedTexture* texture)
{
XLOG("%x removeTexture res: %x...", this, m_texture);
// We update atomically, so paintBitmap() can see the correct value
android::AutoMutex lock(m_atomicSync);
- m_texture = 0;
+ if (m_texture == texture)
+ m_texture = 0;
}
void BaseTile::setScale(float scale)
BackedDoubleBufferedTexture* texture() { return m_texture; }
// TextureOwner implementation
- virtual void removeTexture();
+ virtual void removeTexture(BackedDoubleBufferedTexture* texture);
virtual TiledPage* page() { return m_page; }
private:
}
#endif
-GLWebViewState::GLWebViewState()
+GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
: m_scaleRequestState(kNoScaleRequest)
, m_currentScale(1)
, m_futureScale(1)
, m_baseLayer(0)
, m_currentPictureCounter(0)
, m_usePageA(true)
+ , m_globalButtonMutex(buttonMutex)
{
m_viewport.setEmpty();
m_futureViewportTileBounds.setEmpty();
unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
{
android::Mutex::Autolock lock(m_baseLayerLock);
- if (m_baseLayer)
+ if (m_baseLayer) {
+ m_globalButtonMutex->lock();
m_baseLayer->drawCanvas(canvas);
+ m_globalButtonMutex->unlock();
+ }
return m_currentPictureCounter;
}
};
typedef int32_t GLScaleState;
- GLWebViewState();
+ GLWebViewState(android::Mutex* globalButtonMutex);
~GLWebViewState();
GLScaleState scaleRequestState() const { return m_scaleRequestState; }
void setScaleRequestState(GLScaleState state) { m_scaleRequestState = state; }
TiledPage* m_tiledPageA;
TiledPage* m_tiledPageB;
SkIRect m_lastInval;
+ android::Mutex* m_globalButtonMutex;
};
} // namespace WebCore
m_contentsImage(0),
m_extra(0),
m_uniqueId(++gUniqueId),
- m_texture(0),
- m_pictureUsed(0)
+ m_drawingTexture(0),
+ m_reservedTexture(0),
+ m_pictureUsed(0),
+ m_scale(1)
{
m_backgroundColor = 0;
m_haveClip(layer.m_haveClip),
m_extra(0), // deliberately not copied
m_uniqueId(layer.m_uniqueId),
- m_texture(0)
+ m_drawingTexture(0),
+ m_reservedTexture(0)
{
m_isFixed = layer.m_isFixed;
m_contentsImage = layer.m_contentsImage;
m_childrenTransform = layer.m_childrenTransform;
m_dirty = layer.m_dirty;
m_pictureUsed = layer.m_pictureUsed;
+ m_scale = layer.m_scale;
for (int i = 0; i < layer.countChildren(); i++)
addChild(layer.getChild(i)->copy())->unref();
m_contentsImage(0),
m_extra(0),
m_uniqueId(-1),
- m_texture(0)
+ m_drawingTexture(0),
+ m_reservedTexture(0),
+ m_scale(1)
{
m_backgroundColor = 0;
m_dirty = false;
gDebugLayerAndroidInstances++;
}
+void LayerAndroid::removeTexture(BackedDoubleBufferedTexture* aTexture)
+{
+ LayerTexture* texture = static_cast<LayerTexture*>(aTexture);
+ android::AutoMutex lock(m_atomicSync);
+ if (!texture) { // remove ourself from both textures
+ if (m_drawingTexture)
+ m_drawingTexture->release(this);
+ if (m_reservedTexture &&
+ m_reservedTexture != m_drawingTexture)
+ m_reservedTexture->release(this);
+ } else {
+ if (m_drawingTexture && m_drawingTexture == texture)
+ m_drawingTexture->release(this);
+ if (m_reservedTexture &&
+ m_reservedTexture == texture &&
+ m_reservedTexture != m_drawingTexture)
+ m_reservedTexture->release(this);
+ }
+ if (m_drawingTexture && m_drawingTexture->owner() != this)
+ m_drawingTexture = 0;
+ if (m_reservedTexture && m_reservedTexture->owner() != this)
+ m_reservedTexture = 0;
+}
+
LayerAndroid::~LayerAndroid()
{
- if (m_texture)
- m_texture->release(this);
+ removeTexture(0);
removeChildren();
m_contentsImage->safeUnref();
m_recordingPicture->safeUnref();
localMatrix.setM34(0);
localMatrix.setM43(0);
}
+
// now apply it to our children
if (!m_childrenTransform.isIdentity()) {
for (int i = 0; i < count; i++)
this->getChild(i)->reserveGLTextures();
+ LayerTexture* reservedTexture = 0;
if (needsTexture()) {
- LayerTexture* texture;
- texture = TilesManager::instance()->getExistingTextureForLayer(this);
- // SMP flush
- android::AutoMutex lock(m_atomicSync);
- m_texture = texture;
+ // Compute the layer size & position we need (clipped to the viewport)
+ IntRect r(0, 0, getWidth(), getHeight());
+ IntRect tr = drawTransform().mapRect(r);
+ IntRect cr = TilesManager::instance()->shader()->clippedRectWithViewport(tr);
+ m_layerTextureRect = drawTransform().inverse().mapRect(cr);
+
+ reservedTexture = TilesManager::instance()->getExistingTextureForLayer(this, m_layerTextureRect);
+
+ // If we do not have a drawing texture (i.e. new LayerAndroid tree),
+ // we get any one available.
+ if (!m_drawingTexture)
+ m_drawingTexture = TilesManager::instance()->getExistingTextureForLayer(this, m_layerTextureRect, true);
+ }
+
+ // SMP flush
+ android::AutoMutex lock(m_atomicSync);
+ // we set the reservedTexture if it's different from the drawing texture
+ if (m_reservedTexture != reservedTexture &&
+ ((m_reservedTexture != m_drawingTexture) ||
+ (m_reservedTexture == 0 && m_drawingTexture == 0))) {
+ if (m_reservedTexture)
+ m_reservedTexture->release(this);
+ m_reservedTexture = reservedTexture;
}
}
for (int i = 0; i < count; i++)
this->getChild(i)->createGLTextures();
- if (needsTexture() && !m_texture) {
- LayerTexture* texture;
- texture = TilesManager::instance()->createTextureForLayer(this);
- // SMP flush + keep dirty bit in sync
- android::AutoMutex lock(m_atomicSync);
- m_texture = texture;
- m_dirty = true;
+ if (!needsTexture())
+ return;
+
+ LayerTexture* reservedTexture = m_reservedTexture;
+ if (!reservedTexture)
+ reservedTexture = TilesManager::instance()->createTextureForLayer(this, m_layerTextureRect);
+
+ if (!reservedTexture)
+ return;
+
+ // SMP flush
+ m_atomicSync.lock();
+ m_reservedTexture = reservedTexture;
+ m_atomicSync.unlock();
+
+ if (reservedTexture &&
+ (reservedTexture != m_drawingTexture) &&
+ reservedTexture->isReady()) {
+ if (m_drawingTexture) {
+ TilesManager::instance()->removeOperationsForTexture(m_drawingTexture);
+ m_drawingTexture->release(this);
+ }
+ m_drawingTexture = reservedTexture;
}
- checkForObsolescence();
+ if (!needsScheduleRepaint(reservedTexture))
+ return;
+
+ XLOG("We schedule a paint for layer %d, because isReady %d or m_dirty %d, using texture %x",
+ uniqueId(), m_reservedTexture->isReady(), m_dirty, m_reservedTexture);
+ PaintLayerOperation* operation = new PaintLayerOperation(this);
+ TilesManager::instance()->scheduleOperation(operation);
}
-void LayerAndroid::checkForObsolescence()
+bool LayerAndroid::needsScheduleRepaint(LayerTexture* texture)
{
- m_atomicSync.lock();
- if (!m_texture) {
- m_atomicSync.unlock();
- return;
- }
+ if (!texture)
+ return false;
- if (!m_pictureUsed || m_texture->pictureUsed() != m_pictureUsed) {
+ if (!m_pictureUsed || texture->pictureUsed() != m_pictureUsed) {
XLOG("We mark layer %d as dirty because: m_pictureUsed(%d == 0?), texture picture used %x",
- uniqueId(), m_pictureUsed, m_texture->pictureUsed());
- m_texture->setPictureUsed(m_pictureUsed);
+ uniqueId(), m_pictureUsed, texture->pictureUsed());
+ texture->setPictureUsed(m_pictureUsed);
m_dirty = true;
}
- if (!m_texture->isReady())
+ if (!texture->isReady())
m_dirty = true;
- bool dirty = m_dirty;
- m_atomicSync.unlock();
-
- if (!dirty)
- return;
-
- XLOG("We schedule a paint for layer %d, because isReady %d or m_dirty %d",
- uniqueId(), m_texture->isReady(), m_dirty);
- PaintLayerOperation* operation = new PaintLayerOperation(this);
- TilesManager::instance()->scheduleOperation(operation);
+ return m_dirty;
}
static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b)
TilesManager::instance()->shader()->clip(m_clippingRect);
- if (prepareContext() && m_texture) {
- TextureInfo* textureInfo = m_texture->consumerLock();
- if (textureInfo && m_texture->isReady()) {
- TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), rect,
+ if (prepareContext() && m_drawingTexture) {
+ TextureInfo* textureInfo = m_drawingTexture->consumerLock();
+ if (textureInfo && m_drawingTexture->isReady()) {
+ SkRect bounds;
+ IntRect textureRect = m_drawingTexture->rect();
+ bounds.set(0, 0, textureRect.width(), textureRect.height());
+ // move the drawing depending on where the texture is on the layer
+ TransformationMatrix m = drawTransform();
+ m.translate(textureRect.x(), textureRect.y());
+ TilesManager::instance()->shader()->drawLayerQuad(m, bounds,
textureInfo->m_textureId,
m_drawOpacity);
}
- m_texture->consumerRelease();
+ m_drawingTexture->consumerRelease();
}
// When the layer is dirty, the UI thread should be notified to redraw.
return askPaint;
}
+void LayerAndroid::setScale(float scale)
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->setScale(scale);
+
+ android::AutoMutex lock(m_atomicSync);
+ m_scale = scale;
+}
+
// This is called from the texture generation thread
void LayerAndroid::paintBitmapGL()
{
- XLOG("LayerAndroid paintBitmapGL (layer %d)", uniqueId());
// We acquire the values below atomically. This ensures that we are reading
// values correctly across cores. Further, once we have these values they
// can be updated by other threads without consequence.
m_atomicSync.lock();
- LayerTexture* texture = m_texture;
- m_atomicSync.unlock();
+ LayerTexture* texture = m_reservedTexture;
if (!texture) {
+ m_atomicSync.unlock();
XLOG("Layer %d doesn't have a texture!", uniqueId());
return;
}
+ XLOG("LayerAndroid paintBitmapGL (layer %d), texture used %x", uniqueId(), texture);
+
+ // We need to mark the texture as busy before relinquishing the lock
+ // -- so that TilesManager::cleanupLayersTextures() can check if the texture
+ // is used before trying to destroy it
+ // If LayerAndroid::removeTexture() is called before us, we'd have bailed
+ // out early as texture would have been null; if it is called after us, we'd
+ // have marked the texture has being busy, and the texture will not be
+ // destroy immediately.
texture->producerAcquireContext();
TextureInfo* textureInfo = texture->producerLock();
+ m_atomicSync.unlock();
// at this point we can safely check the ownership (if the texture got
// transferred to another BaseTile under us)
XLOG("LayerAndroid %d paintBitmapGL WE ARE PAINTING", uniqueId());
SkCanvas* canvas = texture->canvas();
+ float scale = texture->scale();
+
+ IntRect textureRect = texture->rect();
+ canvas->save();
canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
+ canvas->scale(scale, scale);
+ canvas->translate(-textureRect.x(), -textureRect.y());
contentDraw(canvas);
+ canvas->restore();
XLOG("LayerAndroid %d paintBitmapGL PAINTING DONE, updating the texture", uniqueId());
m_atomicSync.lock();
virtual ~LayerAndroid();
// TextureOwner methods
- virtual void removeTexture()
- {
- android::AutoMutex lock(m_atomicSync);
- m_texture = 0;
- }
+ virtual void removeTexture(BackedDoubleBufferedTexture* texture);
+
+ LayerTexture* texture() { return m_reservedTexture; }
virtual TiledPage* page() { return 0; }
static int instancesCount();
void createGLTextures();
virtual bool needsTexture();
- void checkForObsolescence();
+ bool needsScheduleRepaint(LayerTexture* texture);
+ void setScale(float scale);
+ float getScale() { return m_scale; }
virtual bool drawGL(SkMatrix&);
bool drawChildrenGL(SkMatrix&);
virtual void paintBitmapGL();
DrawExtra* m_extra;
int m_uniqueId;
- // GL textures management
- LayerTexture* m_texture;
+ // We have two textures pointers -- one if the texture we are currently
+ // using to draw (m_drawingTexture), the other one is the one we get
+ // from trying to reserve a texture from the TilesManager. Usually, they
+ // are identical, but in some cases they are not (different scaling
+ // resulting in the need for different geometry, at initilisation, and
+ // if the texture asked does not fit in memory)
+ LayerTexture* m_drawingTexture;
+ LayerTexture* m_reservedTexture;
+
+ // rect used to query TilesManager for the right texture
+ IntRect m_layerTextureRect;
+
// used to signal that the tile is out-of-date and needs to be redrawn
bool m_dirty;
unsigned int m_pictureUsed;
+ float m_scale;
+
// This mutex serves two purposes. (1) It ensures that certain operations
// happen atomically and (2) it makes sure those operations are synchronized
// across all threads and cores.
#define LayerTexture_h
#include "BackedDoubleBufferedTexture.h"
+#include "IntRect.h"
namespace WebCore {
SkBitmap::Config config = SkBitmap::kARGB_8888_Config)
: BackedDoubleBufferedTexture(w, h, config)
, m_id(0)
+ , m_scale(1)
, m_pictureUsed(0)
, m_textureUpdates(0)
{}
+ virtual ~LayerTexture() {};
int id() { return m_id; }
void setId(int id) { m_id = id; }
void setPictureUsed(unsigned pictureUsed) { m_pictureUsed = pictureUsed; }
bool isReady();
virtual void producerUpdate(TextureInfo* textureInfo);
+ void setRect(const IntRect& r) { m_rect = r; }
+ IntRect& rect() { return m_rect; }
+ float scale() { return m_scale; }
+ void setScale(float scale) { m_scale = scale; }
private:
void update();
int m_id;
+ IntRect m_rect;
+ float m_scale;
unsigned int m_pictureUsed;
unsigned int m_textureUpdates;
};
return m_layer->getRootLayer();
}
-bool PaintLayerFilter::check(QueuedOperation* operation)
+LayerTexture* PaintLayerOperation::texture()
+{
+ if (!m_layer)
+ return 0;
+ return m_layer->texture();
+}
+
+bool PaintLayerBaseFilter::check(QueuedOperation* operation)
{
if (operation->type() == QueuedOperation::PaintLayer) {
PaintLayerOperation* op = static_cast<PaintLayerOperation*>(operation);
}
return false;
}
+
+bool PaintLayerTextureFilter::check(QueuedOperation* operation)
+{
+ if (operation->type() == QueuedOperation::PaintLayer) {
+ PaintLayerOperation* op = static_cast<PaintLayerOperation*>(operation);
+ if (op->texture() == m_texture)
+ return true;
+ }
+ return false;
+}
namespace WebCore {
class LayerAndroid;
+class LayerTexture;
class PaintLayerOperation : public QueuedOperation {
public:
virtual bool operator==(const QueuedOperation* operation);
virtual void run();
SkLayer* baseLayer();
+ LayerAndroid* layer() { return m_layer; }
+ LayerTexture* texture();
private:
LayerAndroid* m_layer;
};
-class PaintLayerFilter : public OperationFilter {
+class PaintLayerBaseFilter : public OperationFilter {
public:
- PaintLayerFilter(SkLayer* layer) : m_baseLayer(layer) {}
+ PaintLayerBaseFilter(SkLayer* layer) : m_baseLayer(layer) {}
virtual bool check(QueuedOperation* operation);
private:
SkLayer* m_baseLayer;
};
+class PaintLayerTextureFilter : public OperationFilter {
+ public:
+ PaintLayerTextureFilter(LayerTexture* texture) : m_texture(texture) {}
+ virtual bool check(QueuedOperation* operation);
+
+ private:
+ LayerTexture* m_texture;
+};
+
}
#endif // PaintLayerOperation_h
m_clipRect = clip;
}
+IntRect ShaderProgram::clippedRectWithViewport(const IntRect& rect)
+{
+ IntRect viewport(m_viewport.fLeft, m_viewport.fTop,
+ m_viewport.width(), m_viewport.height());
+ viewport.intersect(rect);
+ return viewport;
+}
+
void ShaderProgram::drawLayerQuad(const TransformationMatrix& drawMatrix,
SkRect& geometry, int textureId, float opacity)
{
FloatRect clipRectInScreenCoord(const TransformationMatrix& drawMatrix,
const IntSize& size);
void clip(const FloatRect& rect);
+ IntRect clippedRectWithViewport(const IntRect& rect);
private:
GLuint loadShader(GLenum shaderType, const char* pSource);
namespace WebCore {
class TiledPage;
+class BackedDoubleBufferedTexture;
class TextureOwner {
public:
virtual ~TextureOwner() {}
- virtual void removeTexture() = 0;
+ virtual void removeTexture(BackedDoubleBufferedTexture* texture) = 0;
virtual TiledPage* page() = 0;
};
void TexturesGenerator::removeOperationsForBaseLayer(BaseLayerAndroid* layer)
{
- removeOperationsForFilter(new PaintLayerFilter(layer));
+ removeOperationsForFilter(new PaintLayerBaseFilter(layer));
+}
+
+void TexturesGenerator::removeOperationsForTexture(LayerTexture* texture)
+{
+ removeOperationsForFilter(new PaintLayerTextureFilter(texture));
}
void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter)
if (!m_waitForCompletion)
return;
- // At this point, it means that we are currently painting a operation that
- // we want to be removed -- we should wait until it is painted, so that
- // when we return our caller can be sure that there is no more TileSet
- // in the queue for that TiledPage and can safely deallocate the BaseTiles.
+ // At this point, it means that we are currently executing an operation that
+ // we want to be removed -- we should wait until it is done, so that
+ // when we return our caller can be sure that there is no more operations
+ // in the queue matching the given filter.
mRequestedOperationsLock.lock();
+ if (!m_waitForCompletion) {
+ mRequestedOperationsLock.unlock();
+ return; // operation treated
+ }
mRequestedOperationsCond.wait(mRequestedOperationsLock);
m_waitForCompletion = false;
mRequestedOperationsLock.unlock();
#if USE(ACCELERATED_COMPOSITING)
+#include "LayerTexture.h"
#include "QueuedOperation.h"
#include "TileSet.h"
#include "TiledPage.h"
void removeOperationsForPage(TiledPage* page);
void removeOperationsForBaseLayer(BaseLayerAndroid* layer);
+ void removeOperationsForTexture(LayerTexture* texture);
void removeOperationsForFilter(OperationFilter* filter);
void scheduleOperation(QueuedOperation* operation);
m_pixmapsGenerationThread = new TexturesGenerator();
m_pixmapsGenerationThread->run("TexturesGenerator");
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+ m_totalMaxTextureSize = m_maxTextureSize * m_maxTextureSize * BYTES_PER_PIXEL;
+ XLOG("Max texture size %d", m_maxTextureSize);
}
// Has to be run on the texture generation threads
return 0;
}
-LayerTexture* TilesManager::getExistingTextureForLayer(LayerAndroid* layer)
+LayerTexture* TilesManager::getExistingTextureForLayer(LayerAndroid* layer,
+ const IntRect& rect,
+ bool any)
{
android::Mutex::Autolock lock(m_texturesLock);
for (unsigned int i = 0; i< m_layersTextures.size(); i++) {
if (m_layersTextures[i]->id() != layer->uniqueId())
continue;
- if (layer->getSize() != m_layersTextures[i]->getSize())
+ if (!any && rect != m_layersTextures[i]->rect())
+ continue;
+ if (!any && layer->getScale() != m_layersTextures[i]->scale())
continue;
XLOG("return layer %d (%x) for tile %d (%x)",
void TilesManager::printLayersTextures(const char* s)
{
#ifdef DEBUG
+ XLOG(">>> print layers textures (%s)", s);
for (unsigned int i = 0; i< m_layersTextures.size(); i++) {
- XLOG("[%d] %s, texture %x for layer %d, owner: %x", i, s, m_layersTextures[i],
- m_layersTextures[i]->id(), m_layersTextures[i]->owner());
+ XLOG("[%d] %s, texture %x for layer %d (w: %.2f, h: %.2f), owner: %x",
+ i, s, m_layersTextures[i],
+ m_layersTextures[i]->id(),
+ m_layersTextures[i]->getSize().fWidth,
+ m_layersTextures[i]->getSize().fHeight,
+ m_layersTextures[i]->owner());
}
+ XLOG("<<< print layers textures (%s)", s);
#endif
}
-void TilesManager::cleanupLayersTextures(bool forceCleanup)
+void TilesManager::cleanupLayersTextures(LayerAndroid* layer, bool forceCleanup)
{
android::Mutex::Autolock lock(m_texturesLock);
+ SkLayer* rootLayer = layer->getRootLayer();
#ifdef DEBUG
- printLayersTextures("cleanup");
+ if (forceCleanup)
+ XLOG("FORCE cleanup");
+ XLOG("before cleanup, memory %d", m_layersMemoryUsage);
+ printLayersTextures("before cleanup");
#endif
- for (unsigned int i = 0; i< m_layersTextures.size(); i++) {
+ for (unsigned int i = 0; i< m_layersTextures.size();) {
LayerTexture* texture = m_layersTextures[i];
- if (forceCleanup)
- texture->setOwner(0);
+ if (forceCleanup && texture->owner()) {
+ LayerAndroid* textureLayer =
+ static_cast<LayerAndroid*>(texture->owner());
+ if (textureLayer->getRootLayer() != rootLayer) {
+ // We only want to force destroy layers
+ // that are not used by the current page
+ XLOG("force removing texture %x for layer %d",
+ texture, textureLayer->uniqueId());
+ textureLayer->removeTexture(texture);
+ }
+ }
- if (!texture->owner()) {
+ // We only try to destroy textures that have no owners.
+ // This could be due to:
+ // 1) - the LayerAndroid dtor has been called (i.e. when swapping
+ // a LayerAndroid tree with a new one)
+ // 2) - or due to the above code, forcing a destroy.
+ // If the texture has been forced to be released (case #2), it
+ // could still be in use (in the middle of being painted). So we
+ // need to check that's not the case by checking busy(). See
+ // LayerAndroid::paintBitmapGL().
+ if (!texture->owner() && !texture->busy()) {
m_layersMemoryUsage -= (int) texture->getSize().fWidth
* (int) texture->getSize().fHeight * BYTES_PER_PIXEL;
m_layersTextures.remove(i);
+ // We can destroy the texture. We first remove it from the textures
+ // list, and then remove any queued drawing. At this point we know
+ // the texture has been removed from the layer, and that it's not
+ // busy, so it's safe to delete.
+ m_pixmapsGenerationThread->removeOperationsForTexture(texture);
+ XLOG("delete texture %x", texture);
delete texture;
+ } else {
+ // only iterate if we don't delete (if we delete, no need to as we
+ // remove the element from the array)
+ i++;
}
}
+ printLayersTextures("after cleanup");
+ XLOG("after cleanup, memory %d", m_layersMemoryUsage);
}
-LayerTexture* TilesManager::createTextureForLayer(LayerAndroid* layer)
+LayerTexture* TilesManager::createTextureForLayer(LayerAndroid* layer, const IntRect& rect)
{
- int w = layer->getWidth();
- int h = layer->getHeight();
- int size = w * h * BYTES_PER_PIXEL;
+ int w = rect.width() * layer->getScale();
+ int h = rect.height() * layer->getScale();
+ unsigned int size = w * h * BYTES_PER_PIXEL;
+
+ // We will not allocate textures that:
+ // 1) cannot be handled by the graphic card (m_maxTextureSize &
+ // m_totalMaxTextureSize)
+ // 2) will make us go past our texture limit (MAX_LAYERS_ALLOCATION)
+
+ bool large = w > m_maxTextureSize || h > m_maxTextureSize || size > m_totalMaxTextureSize;
+ XLOG("createTextureForLayer(%d) @scale %.2f => %d, %d (too large? %x)", layer->uniqueId(),
+ layer->getScale(), w, h, large);
+
+ // For now just return 0 if too large
+ if (large)
+ return 0;
+
+ if (w == 0 || h == 0) // empty layer
+ return 0;
if (m_layersMemoryUsage + size > MAX_LAYERS_ALLOCATION)
- cleanupLayersTextures(true);
+ cleanupLayersTextures(layer, true);
- android::Mutex::Autolock lock(m_texturesLock);
LayerTexture* texture = new LayerTexture(w, h);
texture->setId(layer->uniqueId());
+ texture->setRect(rect);
+ texture->setScale(layer->getScale());
+
+ android::Mutex::Autolock lock(m_texturesLock);
m_layersTextures.append(texture);
texture->acquire(layer);
m_layersMemoryUsage += size;
m_pixmapsGenerationThread->removeOperationsForBaseLayer(layer);
}
+ void removeOperationsForTexture(LayerTexture* texture)
+ {
+ m_pixmapsGenerationThread->removeOperationsForTexture(texture);
+ }
+
void scheduleOperation(QueuedOperation* operation)
{
m_pixmapsGenerationThread->scheduleOperation(operation);
BackedDoubleBufferedTexture* getAvailableTexture(BaseTile* owner);
void printLayersTextures(const char* s);
- void cleanupLayersTextures(bool forceCleanup = false);
- LayerTexture* getExistingTextureForLayer(LayerAndroid* layer);
- LayerTexture* createTextureForLayer(LayerAndroid* layer);
+ void cleanupLayersTextures(LayerAndroid* layer, bool forceCleanup = false);
+ LayerTexture* getExistingTextureForLayer(LayerAndroid* layer, const IntRect& rect,
+ bool any = false);
+ LayerTexture* createTextureForLayer(LayerAndroid* layer, const IntRect& rect);
void markGeneratorAsReady()
{
android::Mutex::Autolock lock(m_generatorLock);
m_generatorReadyCond.signal();
+ m_generatorReady = true;
}
void printTextures();
void waitForGenerator()
{
android::Mutex::Autolock lock(m_generatorLock);
- m_generatorReadyCond.wait(m_generatorLock);
+ if (!m_generatorReady)
+ m_generatorReadyCond.wait(m_generatorLock);
}
Vector<BackedDoubleBufferedTexture*> m_textures;
Vector<LayerTexture*> m_layersTextures;
unsigned int m_layersMemoryUsage;
+ GLint m_maxTextureSize;
+ unsigned int m_totalMaxTextureSize;
bool m_generatorReady;
if (!node() || !node()->hasTagName(iframeTag))
return false;
+#if PLATFORM(ANDROID)
+ // XXX: Bug submitted to webkit.org
+ // https://bugs.webkit.org/show_bug.cgi?id=52655
+ if (style()->visibility() != VISIBLE)
+ return false;
+#endif
+
// If the contents of the iframe are composited, then we have to be as well.
HTMLIFrameElement* element = static_cast<HTMLIFrameElement*>(node());
if (Document* contentDocument = element->contentDocument()) {
if (layer->isFixed())
compositingState.m_fixedSibling = true;
- if (!willBeComposited && compositingState.m_fixedSibling)
+ if (!willBeComposited && compositingState.m_fixedSibling) {
layer->setMustOverlapCompositedLayers(true);
-
- if (willBeComposited || compositingState.m_fixedSibling) {
-#else
- if (willBeComposited) {
+ willBeComposited = true;
+ }
#endif
+ if (willBeComposited) {
// Tell the parent it has compositing descendants.
compositingState.m_subtreeIsCompositing = true;
// This layer now acts as the ancestor for kids.
void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) {
if (!m_manualLoader)
loader->commitData(data, length);
- else {
+
+ // commit data may have created a manual plugin loader
+ if (m_manualLoader) {
if (!m_hasSentResponseToPlugin) {
m_manualLoader->didReceiveResponse(loader->response());
// Failure could cause the main document to have an error causing
void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) {
ASSERT(m_frame);
ASSERT(item);
- // We should have added a bridge when the child item was added to its
- // parent.
- AndroidWebHistoryBridge* bridge = item->bridge();
- ASSERT(bridge);
// store the current scale (only) for the top frame
if (!m_frame->tree()->parent()) {
+ // We should have added a bridge when the child item was added to its
+ // parent.
+ AndroidWebHistoryBridge* bridge = item->bridge();
+ ASSERT(bridge);
WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
bridge->setScale(webViewCore->scale());
bridge->setTextWrapScale(webViewCore->textWrapScale());
m_currentTime = duration();
m_player->timeChanged();
m_paused = true;
- m_currentTime = 0;
m_hasVideo = false;
m_networkState = MediaPlayer::Idle;
m_readyState = MediaPlayer::HaveNothing;
void MediaPlayerPrivate::onPaused() {
m_paused = true;
- m_currentTime = 0;
m_hasVideo = false;
m_networkState = MediaPlayer::Idle;
m_readyState = MediaPlayer::HaveNothing;
class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
public:
- void load(const String& url) { m_url = url; }
+ void load(const String& url) {
+ m_url = url;
+ // Cheat a bit here to make sure Window.onLoad event can be triggered
+ // at the right time instead of real video play time, since only full
+ // screen video play is supported in Java's VideoView.
+ // See also comments in prepareToPlay function.
+ m_networkState = MediaPlayer::Loading;
+ m_player->networkStateChanged();
+ m_readyState = MediaPlayer::HaveCurrentData;
+ m_player->readyStateChanged();
+ }
+
void play() {
JNIEnv* env = JSC::Bindings::getJNIEnv();
if (!env || !m_url.length() || !m_glue->m_javaProxy)
return;
m_paused = false;
+
+ if (m_currentTime == duration())
+ m_currentTime = 0;
+
jstring jUrl = wtfStringToJstring(env, m_url);
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl, static_cast<jint>(m_currentTime * 1000.0f));
env->DeleteLocalRef(jUrl);
checkException(env);
m_glue = new JavaGlue;
m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
- m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;I)V");
m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)
{
- if (!isActive())
+ if (!isActive() || !size)
return;
// didReceiveData will take a copy of the data
// For data url's
void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str)
{
- if (!isActive())
+ if (!isActive() || !str->size())
return;
// didReceiveData will take a copy of the data
// For special android files
void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector)
{
- if (!isActive())
+ if (!isActive() || !vector->size())
return;
// didReceiveData will take a copy of the data
namespace android {
struct FormManager::FormElement {
- HTMLFormElement* form_element;
- std::vector<HTMLFormControlElement*> control_elements;
+ RefPtr<HTMLFormElement> form_element;
+ std::vector<RefPtr<HTMLFormControlElement> > control_elements;
std::vector<string16> control_values;
};
continue;
FormData form;
- HTMLFormElementToFormData(form_element->form_element, requirements, EXTRACT_VALUE, &form);
+ HTMLFormElementToFormData(form_element->form_element.get(), requirements, EXTRACT_VALUE, &form);
if (form.fields.size() >= kRequiredAutoFillFields)
forms->push_back(form);
}
if (form_element->form_element->document()->frame() != frame)
continue;
- for (std::vector<HTMLFormControlElement*>::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) {
- if (nameForAutoFill(**iter) == nameForAutoFill(*element)) {
+ for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) {
+ HTMLFormControlElement* candidate = iter->get();
+ if (nameForAutoFill(*candidate) == nameForAutoFill(*element)) {
ExtractMask extract_mask = static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
- return HTMLFormElementToFormData(form_element->form_element, requirements, extract_mask, form);
+ return HTMLFormElementToFormData(form_element->form_element.get(), requirements, extract_mask, form);
}
}
}
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
- HTMLFormControlElement* element = form_element->control_elements[i];
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
if (formControlType(*element) == kText) {
HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
- HTMLFormControlElement* element = form_element->control_elements[i];
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
// Only input elements can be previewed.
if (formControlType(*element) != kText)
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
- HTMLFormControlElement* element = form_element->control_elements[i];
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
if (formControlType(*element) != kText)
continue;
bool FormManager::FindCachedFormElementWithNode(Node* node,
FormElement** form_element) {
for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
- for (std::vector<HTMLFormControlElement*>::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) {
- if (*iter == node) {
+ for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) {
+ if (iter->get() == node) {
*form_element = *form_iter;
return true;
}
// one case in the wild where this happens, paypal.com signup form, the fields
// are appended to the end of the form and are not visible.
for (size_t i = 0, j = 0; i < form->control_elements.size() && j < data.fields.size(); ++i) {
- HTMLFormControlElement* element = form->control_elements[i];
+ HTMLFormControlElement* element = form->control_elements[i].get();
string16 element_name = nameForAutoFill(*element);
if (element_name.empty())
BaseLayerAndroid* base = new BaseLayerAndroid();
base->setContent(m_content);
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ // Layout only fails if called during a layout.
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+
#if USE(ACCELERATED_COMPOSITING)
// We set the background color
if (m_mainFrame && m_mainFrame->document()
void WebViewCore::splitContent(PictureSet* content)
{
- bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
- LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
content->split(&m_content);
rebuildPictureSet(&m_content);
content->set(m_content);
return false;
if (!m_glWebViewState) {
- m_glWebViewState = new GLWebViewState();
+ m_glWebViewState = new GLWebViewState(&m_viewImpl->gButtonMutex);
if (m_baseLayer->content()) {
IntRect rect(0, 0, m_baseLayer->content()->width(), m_baseLayer->content()->height());
m_glWebViewState->setBaseLayer(m_baseLayer, rect);