diff options
52 files changed, 1115 insertions, 913 deletions
diff --git a/WebCore/config.h b/WebCore/config.h index 7174c4b..6117fef 100644 --- a/WebCore/config.h +++ b/WebCore/config.h @@ -160,7 +160,7 @@ #define ANDROID_META_SUPPORT // Converts ListBoxes to dropdown popup lists. -#define ANDROID_LISTBOX_USES_MENU_LIST +#define ENABLE_NO_LISTBOX_RENDERING 1 #define ANDROID_MULTIPLE_WINDOWS #define ANDROID_CSS_RING diff --git a/WebCore/html/HTMLOptionElement.h b/WebCore/html/HTMLOptionElement.h index deac66a..c1791d7 100644 --- a/WebCore/html/HTMLOptionElement.h +++ b/WebCore/html/HTMLOptionElement.h @@ -27,13 +27,6 @@ #include "HTMLFormControlElement.h" #include "OptionElement.h" -#if PLATFORM(ANDROID) -namespace android { -class WebViewCore; -class ListBoxReply; -}; -#endif - namespace WebCore { class HTMLSelectElement; @@ -41,11 +34,6 @@ class HTMLSelectElement; class HTMLOptionElement : public HTMLFormControlElement, public OptionElement { friend class HTMLSelectElement; friend class RenderMenuList; -#if PLATFORM(ANDROID) - friend class RenderThemeAndroid; - friend class android::WebViewCore; - friend class android::ListBoxReply; -#endif public: static PassRefPtr<HTMLOptionElement> create(Document*, HTMLFormElement*); diff --git a/WebCore/page/FrameView.cpp b/WebCore/page/FrameView.cpp index 5314a32..1f03599 100644 --- a/WebCore/page/FrameView.cpp +++ b/WebCore/page/FrameView.cpp @@ -1067,6 +1067,29 @@ void FrameView::removeFixedObject() updateCanBlitOnScrollRecursively(); } +#if PLATFORM(ANDROID) +// When the screen size change, fixed positioned element should be updated. +void FrameView::updatePositionedObjects() +{ + RenderBlock::PositionedObjectsListHashSet* positionedObjects = 0; + if (RenderView* root = m_frame->contentRenderer()) + positionedObjects = root->positionedObjects(); + + if (!positionedObjects || positionedObjects->isEmpty()) + return; + + RenderBlock::PositionedObjectsListHashSet::const_iterator end = positionedObjects->end(); + for (RenderBlock::PositionedObjectsListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { + RenderBox* renderBox = *it; + if (renderBox->style()->position() != FixedPosition) + continue; + + renderBox->computeLogicalWidth(); + renderBox->computeLogicalHeight(); + } +} +#endif + bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { const size_t fixedObjectThreshold = 5; diff --git a/WebCore/page/FrameView.h b/WebCore/page/FrameView.h index 1b5b322..4135045 100644 --- a/WebCore/page/FrameView.h +++ b/WebCore/page/FrameView.h @@ -97,6 +97,10 @@ public: bool needsFullRepaint() const { return m_doFullRepaint; } +#if PLATFORM(ANDROID) + void updatePositionedObjects(); +#endif + #if USE(ACCELERATED_COMPOSITING) void updateCompositingLayers(); diff --git a/WebCore/platform/android/PlatformBridge.h b/WebCore/platform/android/PlatformBridge.h index 3f559d5..faa823e 100644 --- a/WebCore/platform/android/PlatformBridge.h +++ b/WebCore/platform/android/PlatformBridge.h @@ -149,8 +149,8 @@ public: static int memoryUsageMB(); static int actualMemoryUsageMB(); - static int visibleScreenWidth(const FrameView*); - static int visibleScreenHeight(const FrameView*); + static int screenWidthInDocCoord(const FrameView*); + static int screenHeightInDocCoord(const FrameView*); }; } diff --git a/WebCore/platform/android/PopupMenuAndroid.cpp b/WebCore/platform/android/PopupMenuAndroid.cpp index 2bae724..f4c351f 100644 --- a/WebCore/platform/android/PopupMenuAndroid.cpp +++ b/WebCore/platform/android/PopupMenuAndroid.cpp @@ -31,7 +31,7 @@ class PopupReply : public android::WebCoreReply { public: - PopupReply(const IntRect& rect, android::WebViewCore* view, PopupMenuClient* client) + PopupReply(const IntRect& rect, android::WebViewCore* view, ListPopupMenuClient* client) : m_rect(rect) , m_viewImpl(view) , m_popupClient(client) @@ -53,9 +53,23 @@ public: m_viewImpl->contentInvalidate(m_rect); } - virtual void replyIntArray(const int*, int) { - // Should never be called. - SkASSERT(false); + virtual void replyIntArray(const int* values, int count) + { + if (m_popupClient) { + m_popupClient->popupDidHide(); + if (0 == count) { + m_popupClient->valueChanged(-1, true); + } else { + for (int i = 0; i < count; i++) { + m_popupClient->listBoxSelectItem(values[i], + i != 0 /* allowMultiplySelection */, + false /* shift */, + i == count - 1 /* fireOnChangeNow */); + } + } + } + if (m_viewImpl) + m_viewImpl->contentInvalidate(m_rect); } void disconnectClient() @@ -67,12 +81,12 @@ private: IntRect m_rect; // FIXME: Do not need this if we handle ChromeClientAndroid::formStateDidChange android::WebViewCore* m_viewImpl; - PopupMenuClient* m_popupClient; + ListPopupMenuClient* m_popupClient; }; namespace WebCore { -PopupMenuAndroid::PopupMenuAndroid(PopupMenuClient* menuList) +PopupMenuAndroid::PopupMenuAndroid(ListPopupMenuClient* menuList) : m_popupClient(menuList) , m_reply(0) { @@ -91,8 +105,7 @@ void PopupMenuAndroid::disconnectClient() m_reply = 0; } } -// Copied from WebViewCore.cpp. Once we move ListBox handling to this class, -// we can remove the one in WebViewCore.cpp. + // Convert a WTF::String into an array of characters where the first // character represents the length, for easy conversion to java. static uint16_t* stringConverter(const WTF::String& text) @@ -122,9 +135,7 @@ void PopupMenuAndroid::show(const IntRect& rect, FrameView* frameView, int) SkTDArray<int> enabledArray; SkTDArray<int> selectedArray; int size = m_popupClient->listSize(); - // If we use this for ListBoxes in addition to MenuLists, we will need to - // account for 'multiple' - bool multiple = false; + bool multiple = m_popupClient->multiple(); for (int i = 0; i < size; i++) { *names.append() = stringConverter(m_popupClient->itemText(i)); if (m_popupClient->itemIsSeparator(i)) { diff --git a/WebCore/platform/android/PopupMenuAndroid.h b/WebCore/platform/android/PopupMenuAndroid.h index 48bce44..6c2c015 100644 --- a/WebCore/platform/android/PopupMenuAndroid.h +++ b/WebCore/platform/android/PopupMenuAndroid.h @@ -34,18 +34,18 @@ class PopupReply; namespace WebCore { class FrameView; -class PopupMenuClient; +class ListPopupMenuClient; class PopupMenuAndroid : public PopupMenu { public: - PopupMenuAndroid(PopupMenuClient*); + PopupMenuAndroid(ListPopupMenuClient*); virtual ~PopupMenuAndroid(); virtual void show(const IntRect&, FrameView*, int); virtual void hide() { } virtual void updateFromElement() { } virtual void disconnectClient(); private: - PopupMenuClient* m_popupClient; + ListPopupMenuClient* m_popupClient; PopupReply* m_reply; }; diff --git a/WebCore/platform/android/RenderThemeAndroid.cpp b/WebCore/platform/android/RenderThemeAndroid.cpp index 87e7a6d..b43e0e6 100644 --- a/WebCore/platform/android/RenderThemeAndroid.cpp +++ b/WebCore/platform/android/RenderThemeAndroid.cpp @@ -351,57 +351,9 @@ void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* sty bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const PaintInfo& info, const IntRect& rect) { - if (!obj->isListBox()) - return true; - - paintCombo(obj, info, rect); - RenderStyle* style = obj->style(); - if (style) - style->setColor(Color::transparent); - Node* node = obj->node(); - if (!node || !node->hasTagName(HTMLNames::selectTag)) - return true; - - HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node); - // The first item may be visible. Make sure it does not draw. - // If it has a style, it overrides the RenderListBox's style, so we - // need to make sure both are set to transparent. - node = select->item(0); - if (node) { - RenderObject* renderer = node->renderer(); - if (renderer) { - RenderStyle* renderStyle = renderer->style(); - if (renderStyle) - renderStyle->setColor(Color::transparent); - } - } - // Find the first selected option, and draw its text. - // FIXME: In a later change, if there is more than one item selected, - // draw a string that says "X items" like iPhone Safari does - int index = select->selectedIndex(); - node = select->item(index); - if (!node || !node->hasTagName(HTMLNames::optionTag)) - return true; - - HTMLOptionElement* option = static_cast<HTMLOptionElement*>(node); - String label = option->textIndentedToRespectGroupLabel(); - SkRect r(rect); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - // Values for text size and positioning determined by trial and error - paint.setTextSize(r.height() - SkIntToScalar(6)); - - SkCanvas* canvas = getCanvasFromInfo(info); - int saveCount = canvas->save(); - r.fRight -= SkIntToScalar(RenderSkinCombo::extraWidth()); - canvas->clipRect(r); - canvas->drawText(label.characters(), label.length() << 1, - r.fLeft + SkIntToScalar(5), r.fBottom - SkIntToScalar(5), paint); - canvas->restoreToCount(saveCount); - - return true; + if (obj->isMenuList()) + paintCombo(obj, info, rect); + return true; } void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const @@ -414,16 +366,7 @@ bool RenderThemeAndroid::paintSearchField(RenderObject*, const PaintInfo&, const return true; } -void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const -{ - style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); - style->setMaxHeight(Length(style->fontSize() + listboxPadding, Fixed)); - // Make webkit draw invisible, since it will simply draw the first element - style->setColor(Color::transparent); - addIntrinsicMargins(style); -} - -static void adjustMenuListStyleCommon(RenderStyle* style, Element* e) +static void adjustMenuListStyleCommon(RenderStyle* style) { // Added to make room for our arrow and make the touch target less cramped. style->setPaddingLeft(Length(RenderSkinCombo::padding(), Fixed)); @@ -432,9 +375,14 @@ static void adjustMenuListStyleCommon(RenderStyle* style, Element* e) style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); } +void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + adjustMenuListButtonStyle(0, style, 0); +} + void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const { - adjustMenuListStyleCommon(style, e); + adjustMenuListStyleCommon(style); addIntrinsicMargins(style); } @@ -450,7 +398,8 @@ bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const PaintInfo& info, return paintCombo(obj, info, rect); } -void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const +void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*, + RenderStyle* style, Element*) const { // Copied from RenderThemeSafari. const float baseFontSize = 11.0f; @@ -468,7 +417,7 @@ void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyl const int padding = 4; style->setPaddingTop(Length(padding, Fixed)); style->setPaddingLeft(Length(padding, Fixed)); - adjustMenuListStyleCommon(style, e); + adjustMenuListStyleCommon(style); } bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect) diff --git a/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp b/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp index fdc6860..d16b53e 100644 --- a/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp +++ b/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp @@ -42,9 +42,13 @@ BackedDoubleBufferedTexture::BackedDoubleBufferedTexture(uint32_t w, uint32_t h, SkBitmap* bitmap, SkBitmap::Config config) : DoubleBufferedTexture(eglGetCurrentContext()) + , m_x(-1) + , m_y(-1) , m_usedLevel(-1) , m_config(config) , m_owner(0) + , m_delayedReleaseOwner(0) + , m_delayedRelease(false) , m_busy(false) { m_size.set(w, h); @@ -115,15 +119,27 @@ TextureInfo* BackedDoubleBufferedTexture::producerLock() void BackedDoubleBufferedTexture::producerRelease() { DoubleBufferedTexture::producerRelease(); - android::Mutex::Autolock lock(m_busyLock); - m_busy = false; + setNotBusy(); } void BackedDoubleBufferedTexture::producerReleaseAndSwap() { DoubleBufferedTexture::producerReleaseAndSwap(); + setNotBusy(); +} + +void BackedDoubleBufferedTexture::setNotBusy() +{ android::Mutex::Autolock lock(m_busyLock); m_busy = false; + if (m_delayedRelease) { + if (m_owner == m_delayedReleaseOwner) { + m_owner->removeOwned(this); + m_owner = 0; + } + m_delayedRelease = false; + m_delayedReleaseOwner = 0; + } } bool BackedDoubleBufferedTexture::busy() @@ -173,24 +189,45 @@ bool BackedDoubleBufferedTexture::setOwner(TextureOwner* owner) { // if the writable texture is busy (i.e. currently being written to) then we // can't change the owner out from underneath that texture - android::Mutex::Autolock lock(m_busyLock); - if (!m_busy) { + m_busyLock.lock(); + bool busy = m_busy; + m_busyLock.unlock(); + if (!busy) { + // if we are not busy we can try to remove the texture from the layer; + // LayerAndroid::removeTexture() is protected by the same lock as + // LayerAndroid::paintBitmapGL(), so either we execute removeTexture() + // first and paintBitmapGL() will bail out, or we execute it after, + // and paintBitmapGL() will mark the texture as busy before + // relinquishing the lock. LayerAndroid::removeTexture() will call + // BackedDoubleBufferedTexture::release(), which will then do nothing + // if the texture is busy and we then don't return true. + bool proceed = true; if (m_owner && m_owner != owner) - m_owner->removeTexture(this); - m_owner = owner; - owner->addOwned(this); - return true; + proceed = m_owner->removeTexture(this); + + if (proceed) { + m_owner = owner; + owner->addOwned(this); + return true; + } } return false; } -void BackedDoubleBufferedTexture::release(TextureOwner* owner) +bool BackedDoubleBufferedTexture::release(TextureOwner* owner) { + android::Mutex::Autolock lock(m_busyLock); if (m_owner == owner) { - m_owner->removeOwned(this); - m_owner = 0; + if (!m_busy) { + m_owner->removeOwned(this); + m_owner = 0; + return true; + } else { + m_delayedRelease = true; + m_delayedReleaseOwner = owner; + } } - + return false; } } // namespace WebCore diff --git a/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h b/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h index eea5807..b1f170b 100644 --- a/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h +++ b/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h @@ -68,7 +68,7 @@ public: // allows consumer thread to assign ownership of the texture to the tile. It // returns false if ownership cannot be transferred because the tile is busy bool acquire(TextureOwner* owner); - void release(TextureOwner* owner); + bool release(TextureOwner* owner); // set the texture owner if not busy. Return false if busy, true otherwise. bool setOwner(TextureOwner* owner); @@ -78,12 +78,19 @@ public: SkCanvas* canvas(); // only used by the producer thread bool busy(); + void setNotBusy(); const SkSize& getSize() const { return m_size; } + int x() { return m_x; } + int y() { return m_y; } + void setTile(int x, int y) { m_x = x; m_y = y; } + private: void destroyTextures(SharedTexture** textures); + int m_x; + int m_y; SkBitmap* m_bitmap; bool m_sharedBitmap; SkSize m_size; @@ -92,6 +99,12 @@ private: SkBitmap::Config m_config; TextureOwner* m_owner; + // When trying to release a texture, we may delay this if the texture is + // currently used (busy being painted). We use the following two variables + // to do so in setNotBusy() + TextureOwner* m_delayedReleaseOwner; + bool m_delayedRelease; + // This values signals that the texture is currently in use by the consumer. // This allows us to prevent the owner of the texture from changing while the // consumer is holding a lock on the texture. diff --git a/WebCore/platform/graphics/android/BaseLayerAndroid.cpp b/WebCore/platform/graphics/android/BaseLayerAndroid.cpp index d7ea5e6..2f0e999 100644 --- a/WebCore/platform/graphics/android/BaseLayerAndroid.cpp +++ b/WebCore/platform/graphics/android/BaseLayerAndroid.cpp @@ -36,8 +36,6 @@ #include <wtf/CurrentTime.h> #endif // USE(ACCELERATED_COMPOSITING) -#define HARDWARE_ACCELERATION - #ifdef DEBUG #include <cutils/log.h> @@ -70,8 +68,9 @@ BaseLayerAndroid::BaseLayerAndroid() BaseLayerAndroid::~BaseLayerAndroid() { -#ifdef HARDWARE_ACCELERATION - TilesManager::instance()->removeOperationsForBaseLayer(this); +#if USE(ACCELERATED_COMPOSITING) + if (TilesManager::hardwareAccelerationEnabled()) + TilesManager::instance()->removeOperationsForBaseLayer(this); #endif m_content.clear(); #ifdef DEBUG_COUNT @@ -312,6 +311,7 @@ bool BaseLayerAndroid::drawGL(IntRect& viewRect, SkRect& visibleRect, scale = m_glWebViewState->futureScale(); } compositedRoot->setScale(scale); + compositedRoot->computeTextureSize(); compositedRoot->reserveGLTextures(); #ifdef DEBUG diff --git a/WebCore/platform/graphics/android/BaseTile.cpp b/WebCore/platform/graphics/android/BaseTile.cpp index 2753fb2..e5275c6 100644 --- a/WebCore/platform/graphics/android/BaseTile.cpp +++ b/WebCore/platform/graphics/android/BaseTile.cpp @@ -107,13 +107,14 @@ void BaseTile::reserveTexture() m_texture = texture; } -void BaseTile::removeTexture(BackedDoubleBufferedTexture* texture) +bool BaseTile::removeTexture(BackedDoubleBufferedTexture* texture) { XLOG("%x removeTexture res: %x... page %x", this, m_texture, m_page); // We update atomically, so paintBitmap() can see the correct value android::AutoMutex lock(m_atomicSync); if (m_texture == texture) m_texture = 0; + return true; } void BaseTile::setScale(float scale) @@ -174,6 +175,9 @@ void BaseTile::draw(float transparency, SkRect& rect) return; } + if (m_texture->x() != m_x || m_texture->y() != m_y) + return; + TextureInfo* textureInfo = m_texture->consumerLock(); if (!textureInfo) { XLOG("%x (%d, %d) trying to draw, but no textureInfo!", this, x(), y()); @@ -198,6 +202,20 @@ bool BaseTile::isTileReady() return !m_dirty; } +void BaseTile::drawTileInfo(SkCanvas* canvas, + BackedDoubleBufferedTexture* texture, + int x, int y, float scale) +{ + SkPaint paint; + char str[256]; + snprintf(str, 256, "(%d,%d) %.2f, tile %x, texture: %x", + x, y, scale, this, texture); + paint.setARGB(255, 0, 0, 0); + canvas->drawText(str, strlen(str), 50, 100, paint); + paint.setARGB(255, 255, 0, 0); + canvas->drawText(str, strlen(str), 51, 101, paint); +} + // This is called from the texture generation thread void BaseTile::paintBitmap() { @@ -218,6 +236,7 @@ void BaseTile::paintBitmap() const int y = m_y; TiledPage* tiledPage = m_page; + texture->producerAcquireContext(); TextureInfo* textureInfo = texture->producerLock(); // at this point we can safely check the ownership (if the texture got @@ -227,8 +246,9 @@ void BaseTile::paintBitmap() return; } - float tileWidth = textureInfo->m_width; - float tileHeight = textureInfo->m_height; + SkSize size = texture->getSize(); + float tileWidth = size.width(); + float tileHeight = size.height(); const float invScale = 1 / scale; float w = tileWidth * invScale; @@ -255,8 +275,10 @@ void BaseTile::paintBitmap() paint.setARGB(128, 0, 0, 255); canvas->drawLine(0, 0, tileWidth, 0, paint); canvas->drawLine(tileWidth, 0, tileWidth, tileHeight, paint); + drawTileInfo(canvas, texture, x, y, scale); #endif + texture->setTile(x, y); texture->producerUpdate(textureInfo); m_atomicSync.lock(); diff --git a/WebCore/platform/graphics/android/BaseTile.h b/WebCore/platform/graphics/android/BaseTile.h index d22849d..af7df3a 100644 --- a/WebCore/platform/graphics/android/BaseTile.h +++ b/WebCore/platform/graphics/android/BaseTile.h @@ -75,6 +75,9 @@ public: // the only thread-safe function called by the background thread void paintBitmap(); + void drawTileInfo(SkCanvas* canvas, + BackedDoubleBufferedTexture* texture, + int x, int y, float scale); void markAsDirty(const unsigned int pictureCount); bool isDirty(); @@ -87,7 +90,7 @@ public: BackedDoubleBufferedTexture* texture() { return m_texture; } // TextureOwner implementation - virtual void removeTexture(BackedDoubleBufferedTexture* texture); + virtual bool removeTexture(BackedDoubleBufferedTexture* texture); virtual TiledPage* page() { return m_page; } private: diff --git a/WebCore/platform/graphics/android/GLWebViewState.cpp b/WebCore/platform/graphics/android/GLWebViewState.cpp index 3c1d40a..5ba094b 100644 --- a/WebCore/platform/graphics/android/GLWebViewState.cpp +++ b/WebCore/platform/graphics/android/GLWebViewState.cpp @@ -108,9 +108,9 @@ void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const IntRect& rect) // We only update the layers if we are not currently // waiting for a tiledPage to be painted if (m_baseLayerUpdate) { + layer->safeRef(); m_currentBaseLayer->safeUnref(); m_currentBaseLayer = layer; - m_currentBaseLayer->safeRef(); } inval(rect); } @@ -118,9 +118,9 @@ void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const IntRect& rect) void GLWebViewState::unlockBaseLayerUpdate() { m_baseLayerUpdate = true; android::Mutex::Autolock lock(m_baseLayerLock); + m_baseLayer->safeRef(); m_currentBaseLayer->safeUnref(); m_currentBaseLayer = m_baseLayer; - m_currentBaseLayer->safeRef(); inval(m_invalidateRect); IntRect empty; m_invalidateRect = empty; @@ -287,6 +287,25 @@ void GLWebViewState::setViewport(SkRect& viewport, float scale) static_cast<int>(floorf(viewport.fTop * invTileContentHeight)), static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)), static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight))); + + int maxTextureCount = (m_viewportTileBounds.width() + TilesManager::instance()->expandedTileBoundsX() * 2 + 1) * + (m_viewportTileBounds.height() + TilesManager::instance()->expandedTileBoundsY() * 2 + 1) * 2; + TilesManager::instance()->setMaxTextureCount(maxTextureCount); + m_tiledPageA->updateBaseTileSize(); + m_tiledPageB->updateBaseTileSize(); +} + +bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, float scale, SkColor color) +{ + m_baseLayerLock.lock(); + BaseLayerAndroid* baseLayer = m_currentBaseLayer; + baseLayer->safeRef(); + m_baseLayerLock.unlock(); + if (!baseLayer) + return false; + bool ret = baseLayer->drawGL(rect, viewport, scale, color); + baseLayer->safeUnref(); + return ret; } } // namespace WebCore diff --git a/WebCore/platform/graphics/android/GLWebViewState.h b/WebCore/platform/graphics/android/GLWebViewState.h index 7fa5e39..2082f2c 100644 --- a/WebCore/platform/graphics/android/GLWebViewState.h +++ b/WebCore/platform/graphics/android/GLWebViewState.h @@ -205,6 +205,9 @@ public: return false; } + bool drawGL(IntRect& rect, SkRect& viewport, + float scale, SkColor color = SK_ColorWHITE); + private: void inval(const IntRect& rect); // caller must hold m_baseLayerLock diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index 5d6d8ff..bee423c 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -157,28 +157,31 @@ LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(), #endif } -void LayerAndroid::removeTexture(BackedDoubleBufferedTexture* aTexture) +bool LayerAndroid::removeTexture(BackedDoubleBufferedTexture* aTexture) { LayerTexture* texture = static_cast<LayerTexture*>(aTexture); android::AutoMutex lock(m_atomicSync); + + bool textureReleased = true; if (!texture) { // remove ourself from both textures if (m_drawingTexture) - m_drawingTexture->release(this); + textureReleased &= m_drawingTexture->release(this); if (m_reservedTexture && m_reservedTexture != m_drawingTexture) - m_reservedTexture->release(this); + textureReleased &= m_reservedTexture->release(this); } else { if (m_drawingTexture && m_drawingTexture == texture) - m_drawingTexture->release(this); + textureReleased &= m_drawingTexture->release(this); if (m_reservedTexture && m_reservedTexture == texture && m_reservedTexture != m_drawingTexture) - m_reservedTexture->release(this); + textureReleased &= m_reservedTexture->release(this); } if (m_drawingTexture && m_drawingTexture->owner() != this) m_drawingTexture = 0; if (m_reservedTexture && m_reservedTexture->owner() != this) m_reservedTexture = 0; + return textureReleased; } LayerAndroid::~LayerAndroid() @@ -585,7 +588,7 @@ bool LayerAndroid::needsTexture() && m_recordingPicture->width() && m_recordingPicture->height()); } -IntRect LayerAndroid::clippedRect() +IntRect LayerAndroid::clippedRect() const { IntRect r(0, 0, getWidth(), getHeight()); IntRect tr = drawTransform().mapRect(r); @@ -600,10 +603,20 @@ bool LayerAndroid::outsideViewport() m_layerTextureRect.height() == 0; } -int LayerAndroid::countTextureSize() +int LayerAndroid::fullTextureSize() const +{ + return getWidth() * m_scale * getHeight() * m_scale * 4; +} + +int LayerAndroid::clippedTextureSize() const { IntRect cr = clippedRect(); - int size = cr.width() * cr.height() * 4; + return cr.width() * cr.height() * 4; +} + +int LayerAndroid::countTextureSize() +{ + int size = clippedTextureSize(); int count = this->countChildren(); for (int i = 0; i < count; i++) size += getChild(i)->countTextureSize(); @@ -619,6 +632,69 @@ int LayerAndroid::nbLayers() return nb; } +void LayerAndroid::collect(Vector<LayerAndroid*>& layers, int& size) +{ + m_layerTextureRect = clippedRect(); + if (!outsideViewport()) { + layers.append(this); + size += fullTextureSize(); + } + int count = this->countChildren(); + for (int i = 0; i < count; i++) + getChild(i)->collect(layers, size); +} + +static inline bool compareLayerFullSize(const LayerAndroid* a, const LayerAndroid* b) +{ + const int sizeA = a->fullTextureSize(); + const int sizeB = b->fullTextureSize(); + return sizeA > sizeB; +} + +void LayerAndroid::computeTextureSize() +{ + // First, we collect the layers, computing m_layerTextureRect + // as being clipped against the viewport + Vector <LayerAndroid*> layers; + int total = 0; + collect(layers, total); + + // Then we sort them by the size the full texture would need + std::stable_sort(layers.begin(), layers.end(), compareLayerFullSize); + + // Now, let's determinate which layer can use a full texture + int max = TilesManager::instance()->maxLayersAllocation(); + int maxLayerSize = TilesManager::instance()->maxLayerAllocation(); + XLOG("*** layers sorted by size ***"); + XLOG("total memory needed: %d bytes (%d Mb), max %d Mb", + total, total / 1024 / 1024, max / 1024 / 1024); + for (unsigned int i = 0; i < layers.size(); i++) { + LayerAndroid* layer = layers[i]; + bool clipped = true; + // If we are under the maximum, and the layer inspected + // needs a texture less than the maxLayerSize, use the full texture. + if ((total < max) && + (layer->fullTextureSize() < maxLayerSize) && + (layer->getWidth() * m_scale < TilesManager::instance()->getMaxTextureSize()) && + (layer->getHeight() * m_scale < TilesManager::instance()->getMaxTextureSize())) { + IntRect full(0, 0, layer->getWidth(), layer->getHeight()); + layer->m_layerTextureRect = full; + clipped = false; + } else { + // Otherwise, the layer is clipped; update the total + total -= layer->fullTextureSize(); + total += layer->clippedTextureSize(); + } + XLOG("Layer %d (%.2f, %.2f) %d bytes (clipped: %s)", + layer->uniqueId(), layer->getWidth(), layer->getHeight(), + layer->fullTextureSize(), + clipped ? "YES" : "NO"); + } + XLOG("total memory used after clipping: %d bytes (%d Mb), max %d Mb", + total, total / 1024 / 1024, max / 1024 / 1024); + XLOG("*** end of sorted layers ***"); +} + void LayerAndroid::showLayers(int indent) { IntRect cr = clippedRect(); @@ -655,14 +731,10 @@ void LayerAndroid::reserveGLTextures() if (!needsTexture()) return; - LayerTexture* reservedTexture = 0; - - // Compute the layer size & position we need (clipped to the viewport) - m_layerTextureRect = clippedRect(); - if (outsideViewport()) return; + LayerTexture* reservedTexture = 0; reservedTexture = TilesManager::instance()->getExistingTextureForLayer( this, m_layerTextureRect); @@ -682,9 +754,11 @@ void LayerAndroid::reserveGLTextures() 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) || + ((reservedTexture != m_drawingTexture) || (m_reservedTexture == 0 && m_drawingTexture == 0))) { - if (m_reservedTexture) + // Call release on the reserved texture if it is not the same as the + // drawing texture. + if (m_reservedTexture && (m_reservedTexture != m_drawingTexture)) m_reservedTexture->release(this); m_reservedTexture = reservedTexture; } @@ -857,7 +931,7 @@ void LayerAndroid::paintBitmapGL() // 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. + // destroyed immediately. texture->producerAcquireContext(); TextureInfo* textureInfo = texture->producerLock(); m_atomicSync.unlock(); diff --git a/WebCore/platform/graphics/android/LayerAndroid.h b/WebCore/platform/graphics/android/LayerAndroid.h index f4fea49..2cb56c1 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.h +++ b/WebCore/platform/graphics/android/LayerAndroid.h @@ -94,7 +94,7 @@ public: virtual ~LayerAndroid(); // TextureOwner methods - virtual void removeTexture(BackedDoubleBufferedTexture* texture); + virtual bool removeTexture(BackedDoubleBufferedTexture* texture); LayerTexture* texture() { return m_reservedTexture; } virtual TiledPage* page() { return 0; } @@ -102,7 +102,7 @@ public: void setTransform(const TransformationMatrix& matrix) { m_transform = matrix; } FloatPoint translation() const; SkRect bounds() const; - IntRect clippedRect(); + IntRect clippedRect() const; bool outsideViewport(); // Debug/info functions @@ -110,6 +110,13 @@ public: int nbLayers(); void showLayers(int indent = 0); + // Texture size functions + void computeTextureSize(); + void collect(Vector<LayerAndroid*>& layers, + int& size); + int clippedTextureSize() const; + int fullTextureSize() const; + // called on the root layer void reserveGLTextures(); void createGLTextures(); diff --git a/WebCore/platform/graphics/android/MediaLayer.cpp b/WebCore/platform/graphics/android/MediaLayer.cpp index fac94f5..7a4c02d 100644 --- a/WebCore/platform/graphics/android/MediaLayer.cpp +++ b/WebCore/platform/graphics/android/MediaLayer.cpp @@ -40,11 +40,11 @@ namespace WebCore { -MediaLayer::MediaLayer() : LayerAndroid(false) +MediaLayer::MediaLayer(jobject weakWebViewRef) : LayerAndroid(false) { m_bufferedTexture = new MediaTexture(EGL_NO_CONTEXT); m_bufferedTexture->incStrong(this); - m_videoTexture = new VideoTexture(); + m_videoTexture = new VideoTexture(weakWebViewRef); m_videoTexture->incStrong(this); m_currentTextureInfo = 0; @@ -78,6 +78,8 @@ bool MediaLayer::drawGL(SkMatrix& matrix) // draw any video content if present m_videoTexture->drawVideo(drawTransform()); + bool needsInval = true; + // draw the primary content if (m_bufferedTexture) { TextureInfo* textureInfo = m_bufferedTexture->consumerLock(); @@ -95,14 +97,13 @@ bool MediaLayer::drawGL(SkMatrix& matrix) TilesManager::instance()->shader()->drawLayerQuad(m, rect, textureInfo->m_textureId, 1.0f); //TODO fix this m_drawOpacity + if (!rect.isEmpty()) + needsInval = false; } m_bufferedTexture->consumerRelease(); } - drawChildrenGL(matrix); - - //TODO allow plugins to specify when they should be drawn - return true; + return drawChildrenGL(matrix) || needsInval; } ANativeWindow* MediaLayer::acquireNativeWindowForVideo() diff --git a/WebCore/platform/graphics/android/MediaLayer.h b/WebCore/platform/graphics/android/MediaLayer.h index dec6427..15bd6d8 100644 --- a/WebCore/platform/graphics/android/MediaLayer.h +++ b/WebCore/platform/graphics/android/MediaLayer.h @@ -21,6 +21,7 @@ #include "MediaTexture.h" #include "LayerAndroid.h" +#include <jni.h> namespace android { class SurfaceTexture; @@ -31,7 +32,7 @@ namespace WebCore { class MediaLayer : public LayerAndroid { public: - MediaLayer(); + MediaLayer(jobject weakWebViewRef); MediaLayer(const MediaLayer& layer); virtual ~MediaLayer(); diff --git a/WebCore/platform/graphics/android/MediaTexture.cpp b/WebCore/platform/graphics/android/MediaTexture.cpp index 8481a20..a92b570 100644 --- a/WebCore/platform/graphics/android/MediaTexture.cpp +++ b/WebCore/platform/graphics/android/MediaTexture.cpp @@ -24,6 +24,8 @@ #include <gui/SurfaceTexture.h> #include <gui/SurfaceTextureClient.h> #include <wtf/CurrentTime.h> +#include <JNIUtility.h> +#include "WebCoreJni.h" #define LAYER_DEBUG #undef LAYER_DEBUG @@ -45,8 +47,46 @@ namespace WebCore { -VideoTexture::VideoTexture() +class VideoListener : public android::SurfaceTexture::FrameAvailableListener { + +public: + VideoListener(jobject weakWebViewRef) + : m_weakWebViewRef(weakWebViewRef) + , m_postInvalMethod(0) + { + if (!m_weakWebViewRef) + return; + + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef); + if (localWebViewRef) { + jclass wvClass = env->GetObjectClass(localWebViewRef); + m_postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V"); + env->DeleteLocalRef(wvClass); + env->DeleteLocalRef(localWebViewRef); + } + checkException(env); + } + + virtual void onFrameAvailable() + { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef); + if (localWebViewRef) { + env->CallVoidMethod(localWebViewRef, m_postInvalMethod); + env->DeleteLocalRef(localWebViewRef); + } + checkException(env); + } + +private: + jobject m_weakWebViewRef; + jmethodID m_postInvalMethod; +}; + +VideoTexture::VideoTexture(jobject weakWebViewRef) : android::LightRefBase<VideoTexture>() { + m_weakWebViewRef = weakWebViewRef; m_textureId = 0; m_dimensions.setEmpty(); m_newWindowRequest = false; @@ -58,6 +98,10 @@ VideoTexture::~VideoTexture() releaseNativeWindow(); if (m_textureId) glDeleteTextures(1, &m_textureId); + if (m_weakWebViewRef) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + env->DeleteWeakGlobalRef(m_weakWebViewRef); + } } void VideoTexture::initNativeWindowIfNeeded() @@ -73,6 +117,11 @@ void VideoTexture::initNativeWindowIfNeeded() m_surfaceTexture = new android::SurfaceTexture(m_textureId); m_surfaceTextureClient = new android::SurfaceTextureClient(m_surfaceTexture); + + //setup callback + sp<VideoListener> listener = new VideoListener(m_weakWebViewRef); + m_surfaceTexture->setFrameAvailableListener(listener); + m_newWindowRequest = false; m_newWindowReady = true; m_newVideoRequestCond.signal(); @@ -109,8 +158,23 @@ ANativeWindow* VideoTexture::requestNewWindow() } m_newWindowRequest = true; + + // post an inval message to the UI thread to fulfill the request + if (m_weakWebViewRef) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef); + if (localWebViewRef) { + jclass wvClass = env->GetObjectClass(localWebViewRef); + jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V"); + env->CallVoidMethod(localWebViewRef, postInvalMethod); + env->DeleteLocalRef(wvClass); + env->DeleteLocalRef(localWebViewRef); + } + checkException(env); + } + //block until the request can be fulfilled or we time out - m_newVideoRequestCond.waitRelative(m_videoLock, 1000000000); // 1 sec + m_newVideoRequestCond.waitRelative(m_videoLock, 500000000); // .5 sec if (m_surfaceTextureClient.get()) m_newWindowReady = false; @@ -128,6 +192,11 @@ void VideoTexture::releaseNativeWindow() { android::Mutex::Autolock lock(m_videoLock); m_dimensions.setEmpty(); + + if (m_surfaceTexture.get()) + m_surfaceTexture->setFrameAvailableListener(0); + + // clear the strong pointer references m_surfaceTextureClient.clear(); m_surfaceTexture.clear(); } diff --git a/WebCore/platform/graphics/android/MediaTexture.h b/WebCore/platform/graphics/android/MediaTexture.h index 156a67f..189905c 100644 --- a/WebCore/platform/graphics/android/MediaTexture.h +++ b/WebCore/platform/graphics/android/MediaTexture.h @@ -23,6 +23,7 @@ #include "DoubleBufferedTexture.h" #include "LayerAndroid.h" #include <utils/RefBase.h> +#include <jni.h> namespace android { class SurfaceTexture; @@ -40,7 +41,7 @@ public: class VideoTexture : public android::LightRefBase<VideoTexture> { public: - VideoTexture(); + VideoTexture(jobject weakWebViewRef); ~VideoTexture(); void initNativeWindowIfNeeded(); @@ -60,6 +61,8 @@ private: bool m_newWindowRequest; bool m_newWindowReady; + jobject m_weakWebViewRef; + android::Mutex m_videoLock; android::Condition m_newVideoRequestCond; }; diff --git a/WebCore/platform/graphics/android/ShaderProgram.cpp b/WebCore/platform/graphics/android/ShaderProgram.cpp index 8da6855..c4129f1 100644 --- a/WebCore/platform/graphics/android/ShaderProgram.cpp +++ b/WebCore/platform/graphics/android/ShaderProgram.cpp @@ -140,8 +140,6 @@ GLuint ShaderProgram::createProgram(const char* pVertexSource, const char* pFrag } glDeleteProgram(program); program = -1; - } else { - XLOG("couldn't link the shader!"); } } return program; @@ -156,15 +154,21 @@ void ShaderProgram::init() { m_program = createProgram(gVertexShader, gFragmentShader); m_videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader); + if (m_program == -1 || m_videoProgram == -1) + return; m_hProjectionMatrix = glGetUniformLocation(m_program, "projectionMatrix"); m_hAlpha = glGetUniformLocation(m_program, "alpha"); m_hTexSampler = glGetUniformLocation(m_program, "s_texture"); + m_hPosition = glGetAttribLocation(m_program, "vPosition"); + m_hVideoProjectionMatrix = glGetUniformLocation(m_videoProgram, "projectionMatrix"); m_hVideoTextureMatrix = glGetUniformLocation(m_videoProgram, "textureMatrix"); m_hVideoTexSampler = glGetUniformLocation(m_videoProgram, "s_yuvTexture"); + m_hVideoPosition = glGetAttribLocation(m_program, "vPosition"); + const GLfloat coord[] = { 0.0f, 0.0f, // C 1.0f, 0.0f, // D @@ -214,9 +218,8 @@ void ShaderProgram::drawQuad(SkRect& geometry, int textureId, float opacity) glBindTexture(GL_TEXTURE_2D, textureId); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); - glBindAttribLocation(program(), 1, "vPosition"); + glEnableVertexAttribArray(m_hPosition); + glVertexAttribPointer(m_hPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); glUniform1f(alpha(), opacity); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -290,9 +293,8 @@ void ShaderProgram::drawLayerQuad(const TransformationMatrix& drawMatrix, glBindTexture(GL_TEXTURE_2D, textureId); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); - glBindAttribLocation(program(), 1, "vPosition"); + glEnableVertexAttribArray(m_hPosition); + glVertexAttribPointer(m_hPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); glUniform1f(alpha(), opacity); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -318,9 +320,8 @@ void ShaderProgram::drawVideoLayerQuad(const TransformationMatrix& drawMatrix, glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); - glBindAttribLocation(m_videoProgram, 1, "vPosition"); + glEnableVertexAttribArray(m_hVideoPosition); + glVertexAttribPointer(m_hVideoPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); diff --git a/WebCore/platform/graphics/android/ShaderProgram.h b/WebCore/platform/graphics/android/ShaderProgram.h index 1f94290..5e2045c 100644 --- a/WebCore/platform/graphics/android/ShaderProgram.h +++ b/WebCore/platform/graphics/android/ShaderProgram.h @@ -72,6 +72,10 @@ class ShaderProgram { int m_hVideoProjectionMatrix; int m_hVideoTextureMatrix; int m_hVideoTexSampler; + + // attribs + GLint m_hPosition; + GLint m_hVideoPosition; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/android/TextureOwner.h b/WebCore/platform/graphics/android/TextureOwner.h index c421e8a..684efac 100644 --- a/WebCore/platform/graphics/android/TextureOwner.h +++ b/WebCore/platform/graphics/android/TextureOwner.h @@ -36,7 +36,7 @@ class BackedDoubleBufferedTexture; class TextureOwner { public: virtual ~TextureOwner(); - virtual void removeTexture(BackedDoubleBufferedTexture* texture) = 0; + virtual bool removeTexture(BackedDoubleBufferedTexture* texture) = 0; virtual TiledPage* page() = 0; void addOwned(BackedDoubleBufferedTexture*); diff --git a/WebCore/platform/graphics/android/TexturesGenerator.cpp b/WebCore/platform/graphics/android/TexturesGenerator.cpp index cdf4b5a..5b9c809 100644 --- a/WebCore/platform/graphics/android/TexturesGenerator.cpp +++ b/WebCore/platform/graphics/android/TexturesGenerator.cpp @@ -126,10 +126,6 @@ void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter) status_t TexturesGenerator::readyToRun() { - TilesManager::instance()->enableTextures(); - XLOG("Textures enabled (context acquired...)"); - TilesManager::instance()->paintTexturesDefault(); - XLOG("Textures painted"); TilesManager::instance()->markGeneratorAsReady(); XLOG("Thread ready to run"); return NO_ERROR; diff --git a/WebCore/platform/graphics/android/TiledPage.cpp b/WebCore/platform/graphics/android/TiledPage.cpp index 68f38ab..620aa6f 100644 --- a/WebCore/platform/graphics/android/TiledPage.cpp +++ b/WebCore/platform/graphics/android/TiledPage.cpp @@ -49,18 +49,30 @@ #endif // DEBUG +#define MAX_TILES 256 + namespace WebCore { using namespace android; TiledPage::TiledPage(int id, GLWebViewState* state) - : m_id(id) + : m_baseTiles(0) + , m_baseTileSize(0) + , m_id(id) , m_scale(1) , m_invScale(1) , m_glWebViewState(state) , m_latestPictureInval(0) , m_prepare(false) { + m_baseTiles = new BaseTile[MAX_TILES]; +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("TiledPage"); +#endif +} + +void TiledPage::updateBaseTileSize() +{ // This value must be at least 1 greater than the max number of allowed // textures. This is because prepare() asks for a tile before it reserves // a texture for that tile. If all textures are currently in use by the @@ -69,12 +81,10 @@ TiledPage::TiledPage(int id, GLWebViewState* state) // to reserveTexture() will cause some other tile in the page to lose it's // texture and become available, thus ensuring that we always have at least // one tile that is available. - m_baseTileSize = TilesManager::maxTextureCount() + 1; - m_baseTiles = new BaseTile[m_baseTileSize]; - -#ifdef DEBUG_COUNT - ClassTracker::instance()->increment("TiledPage"); -#endif + int baseTileSize = TilesManager::instance()->maxTextureCount() + 1; + if (baseTileSize > m_baseTileSize) + m_baseTileSize = baseTileSize; + XLOG("Allocate %d tiles", m_baseTileSize); } TiledPage::~TiledPage() @@ -154,18 +164,20 @@ void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y availableTile = &tile; } - if (!currentTile) { + if (!currentTile && availableTile) { currentTile = availableTile; currentTile->setContents(this, x, y); } - currentTile->setScale(m_scale); + if (currentTile) { + currentTile->setScale(m_scale); - // ensure there is a texture associated with the tile and then check to - // see if the texture is dirty and in need of repainting - currentTile->reserveTexture(); - if (currentTile->isDirty()) - set->add(currentTile); + // ensure there is a texture associated with the tile and then check to + // see if the texture is dirty and in need of repainting + currentTile->reserveTexture(); + if (currentTile->isDirty()) + set->add(currentTile); + } } } @@ -210,7 +222,6 @@ void TiledPage::updateTileState(const SkIRect& tileBounds) int d = std::max(dx, dy); - XLOG("setTileLevel tile: %x, fxy(%d, %d), level: %d", tile, tileBounds.fLeft, tileBounds.fTop, d); tile.setUsedLevel(d); } @@ -233,43 +244,29 @@ void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBound int nbTilesWidth = tileBounds.width(); int nbTilesHeight = tileBounds.height(); - const int lastTileX = tileBounds.fRight - 1; - const int lastTileY = tileBounds.fBottom - 1; + int lastTileX = tileBounds.fRight - 1; + int lastTileY = tileBounds.fBottom - 1; const int baseContentHeight = m_glWebViewState->baseContentHeight(); const int baseContentWidth = m_glWebViewState->baseContentWidth(); TileSet* highResSet = new TileSet(this, nbTilesHeight, nbTilesWidth); + // Expand number of tiles to allow tiles outside of viewport to be prepared for + // smoother scrolling. int nTilesToPrepare = nbTilesWidth * nbTilesHeight; int nMaxTilesPerPage = m_baseTileSize / 2; - - // PREPARE OFF-SCREEN TILES FOR SMOOTHER SCROLLING - // if you are going down and you are not already at the bottom of the page - // go ahead and prepare the tiles just off-screen beneath the viewport. - // Ensure we have enough tiles to do this with. - if (nTilesToPrepare + nbTilesWidth <= nMaxTilesPerPage) { - if (goingDown && baseContentHeight > lastTileY * TilesManager::tileHeight()) - nbTilesHeight++; - // if you are going up and you are not already at the top of the page go - // ahead and prepare the tiles just off-screen above the viewport. - else if (!goingDown && firstTileY > 0) { - firstTileY--; - nbTilesHeight++; - } + int expandX = TilesManager::instance()->expandedTileBoundsX(); + int expandY = TilesManager::instance()->expandedTileBoundsY(); + if (nTilesToPrepare + (nbTilesHeight * expandX * 2) <= nMaxTilesPerPage) { + firstTileX -= expandX; + lastTileX += expandX; + nbTilesWidth += expandX * 2; } - - if (nTilesToPrepare + nbTilesHeight <= nMaxTilesPerPage) { - // if you are going right and you are not already at the edge of the page go - // ahead and prepare the tiles just off-screen to the right of the viewport. - if (!goingLeft && baseContentWidth > lastTileX * TilesManager::tileWidth()) - nbTilesWidth++; - // if you are going left and you are not already at the edge of the page go - // ahead and prepare the tiles just off-screen to the left of the viewport. - else if (goingLeft && firstTileX > 0) { - firstTileX--; - nbTilesWidth++; - } + if (nTilesToPrepare + (nbTilesWidth * expandY * 2) <= nMaxTilesPerPage) { + firstTileY -= expandY; + lastTileY += expandY; + nbTilesHeight += expandY * 2; } // We chose to prepare tiles depending on the scroll direction. Tiles are @@ -320,10 +317,16 @@ void TiledPage::draw(float transparency, const SkIRect& tileBounds) const float tileWidth = TilesManager::tileWidth() * m_invScale; const float tileHeight = TilesManager::tileHeight() * m_invScale; + SkIRect actualTileBounds = tileBounds; + actualTileBounds.fTop -= TilesManager::instance()->expandedTileBoundsY(); + actualTileBounds.fBottom += TilesManager::instance()->expandedTileBoundsY(); + actualTileBounds.fLeft -= TilesManager::instance()->expandedTileBoundsX(); + actualTileBounds.fRight += TilesManager::instance()->expandedTileBoundsX(); + XLOG("WE DRAW %x (%.2f) with transparency %.2f", this, scale(), transparency); for (int j = 0; j < m_baseTileSize; j++) { BaseTile& tile = m_baseTiles[j]; - if (tileBounds.contains(tile.x(), tile.y())) { + if (actualTileBounds.contains(tile.x(), tile.y())) { SkRect rect; rect.fLeft = tile.x() * tileWidth; diff --git a/WebCore/platform/graphics/android/TiledPage.h b/WebCore/platform/graphics/android/TiledPage.h index 6424f34..e449107 100644 --- a/WebCore/platform/graphics/android/TiledPage.h +++ b/WebCore/platform/graphics/android/TiledPage.h @@ -73,6 +73,8 @@ public: void invalidateRect(const IntRect& invalRect, const unsigned int pictureCount); void setUsable(bool usable); + void updateBaseTileSize(); + private: void updateTileState(const SkIRect& tileBounds); void prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, TileSet* set); diff --git a/WebCore/platform/graphics/android/TilesManager.cpp b/WebCore/platform/graphics/android/TilesManager.cpp index 4b53a54..571d9cc 100644 --- a/WebCore/platform/graphics/android/TilesManager.cpp +++ b/WebCore/platform/graphics/android/TilesManager.cpp @@ -54,20 +54,34 @@ // one viewport, otherwise the allocation may stall. // We need n textures for one TiledPage, and another n textures for the // second page used when scaling. -// In our case, we use 300x300 textures. On the tablet, this equals to -// at least 24 (6 * 4) textures, hence 48. -#define MAX_TEXTURE_ALLOCATION 48 +// In our case, we use 300x300 textures. On the tablet, this equates to +// at least 5 * 3 = 15 textures. We can also enable offscreen textures +#define EXPANDED_TILE_BOUNDS_X 1 +#define EXPANDED_TILE_BOUNDS_Y 4 +#define MAX_TEXTURE_ALLOCATION (5+EXPANDED_TILE_BOUNDS_X*2)*(3+EXPANDED_TILE_BOUNDS_Y*2)*2 #define TILE_WIDTH 300 #define TILE_HEIGHT 300 // Define a maximum amount of ram used by layers -#define MAX_LAYERS_ALLOCATION 20971520 // 20Mb +#define MAX_LAYERS_ALLOCATION 33554432 // 32Mb +// Define a maximum amount of ram used by one layer +#define MAX_LAYER_ALLOCATION 8388608 // 8Mb #define BYTES_PER_PIXEL 4 // 8888 config namespace WebCore { +GLint TilesManager::getMaxTextureSize() +{ + static GLint maxTextureSize = 0; + if (!maxTextureSize) + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + return maxTextureSize; +} + TilesManager::TilesManager() : m_layersMemoryUsage(0) + , m_maxTextureCount(0) + , m_expandedTileBounds(false) , m_generatorReady(false) { XLOG("TilesManager ctor"); @@ -76,33 +90,27 @@ TilesManager::TilesManager() m_tilesBitmap->setConfig(SkBitmap::kARGB_8888_Config, tileWidth(), tileHeight()); m_tilesBitmap->allocPixels(); m_tilesBitmap->eraseColor(0); - for (int i = 0; i < MAX_TEXTURE_ALLOCATION; i++) { - BackedDoubleBufferedTexture* texture = new BackedDoubleBufferedTexture( - tileWidth(), tileHeight(), m_tilesBitmap); - // the atomic load ensures that the texture has been fully initialized - // before we pass a pointer for other threads to operate on - m_textures.append(reinterpret_cast<BackedDoubleBufferedTexture*>( - android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)))); - } - XLOG("TilesManager ctor - init textures done"); - 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 -void TilesManager::enableTextures() +void TilesManager::allocateTiles() { - android::Mutex::Autolock lock(m_texturesLock); - for (unsigned int i = 0; i < m_textures.size(); i++) { - BackedDoubleBufferedTexture* texture = m_textures[i]; - texture->producerAcquireContext(); + int nbTexturesToAllocate = m_maxTextureCount - m_textures.size(); + XLOG("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_maxTextureCount); + int nbTexturesAllocated = 0; + for (int i = 0; i < nbTexturesToAllocate; i++) { + BackedDoubleBufferedTexture* texture = new BackedDoubleBufferedTexture( + tileWidth(), tileHeight(), m_tilesBitmap); + // the atomic load ensures that the texture has been fully initialized + // before we pass a pointer for other threads to operate on + BackedDoubleBufferedTexture* loadedTexture = + reinterpret_cast<BackedDoubleBufferedTexture*>( + android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); + m_textures.append(loadedTexture); + nbTexturesAllocated++; } - XLOG("enableTextures"); + XLOG("allocated %d textures", nbTexturesAllocated); } void TilesManager::printTextures() @@ -128,36 +136,6 @@ void TilesManager::printTextures() #endif // DEBUG } -void TilesManager::paintTexturesDefault() -{ - android::Mutex::Autolock lock(m_texturesLock); - for (unsigned int i = 0; i < m_textures.size(); i++) { - for (int j = 0; j < 2; j++) { - BackedDoubleBufferedTexture* texture = m_textures[i]; - TextureInfo* textureInfo = texture->producerLock(); - SkCanvas* canvas = texture->canvas(); -#ifdef DEBUG - if (j) - canvas->drawARGB(255, 0, 0, 255); - else - canvas->drawARGB(255, 255, 0, 255); - SkPaint paint; - paint.setARGB(128, 255, 0, 0); - paint.setStrokeWidth(3); - canvas->drawLine(0, 0, tileWidth(), tileHeight(), paint); - paint.setARGB(128, 0, 255, 0); - canvas->drawLine(0, tileHeight(), tileWidth(), 0, paint); - paint.setARGB(128, 0, 0, 255); - canvas->drawLine(0, 0, tileWidth(), 0, paint); - canvas->drawLine(tileWidth(), 0, tileWidth(), tileHeight(), paint); -#else - canvas->drawARGB(255, 255, 255, 255); -#endif // DEBUG - texture->producerUpdate(textureInfo); - } - } -} - void TilesManager::resetTextureUsage(TiledPage* page) { android::Mutex::Autolock lock(m_texturesLock); @@ -340,11 +318,13 @@ LayerTexture* TilesManager::createTextureForLayer(LayerAndroid* layer, const Int 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) + // 1) cannot be handled by the graphic card (maxTextureSize & + // totalMaxTextureSize) // 2) will make us go past our texture limit (MAX_LAYERS_ALLOCATION) - bool large = w > m_maxTextureSize || h > m_maxTextureSize || size > m_totalMaxTextureSize; + GLint maxTextureSize = getMaxTextureSize(); + unsigned totalMaxTextureSize = maxTextureSize * maxTextureSize * BYTES_PER_PIXEL; + bool large = w > maxTextureSize || h > maxTextureSize || size > totalMaxTextureSize; XLOG("createTextureForLayer(%d) @scale %.2f => %d, %d (too large? %x)", layer->uniqueId(), layer->getScale(), w, h, large); @@ -370,9 +350,31 @@ LayerTexture* TilesManager::createTextureForLayer(LayerAndroid* layer, const Int return texture; } +int TilesManager::maxLayersAllocation() +{ + return MAX_LAYERS_ALLOCATION; +} + +int TilesManager::maxLayerAllocation() +{ + return MAX_LAYER_ALLOCATION; +} + int TilesManager::maxTextureCount() { - return MAX_TEXTURE_ALLOCATION; + android::Mutex::Autolock lock(m_texturesLock); + return m_maxTextureCount; +} + +void TilesManager::setMaxTextureCount(int max) +{ + XLOG("setMaxTextureCount: %d", max); + if (m_maxTextureCount >= max && m_maxTextureCount) + return; + + android::Mutex::Autolock lock(m_texturesLock); + m_maxTextureCount = max; + allocateTiles(); } float TilesManager::tileWidth() @@ -385,6 +387,14 @@ float TilesManager::tileHeight() return TILE_HEIGHT; } +int TilesManager::expandedTileBoundsX() { + return m_expandedTileBounds ? EXPANDED_TILE_BOUNDS_X : 0; +} + +int TilesManager::expandedTileBoundsY() { + return m_expandedTileBounds ? EXPANDED_TILE_BOUNDS_Y : 0; +} + TilesManager* TilesManager::instance() { if (!gInstance) { diff --git a/WebCore/platform/graphics/android/TilesManager.h b/WebCore/platform/graphics/android/TilesManager.h index 6cd8bcd..eeb38fe 100644 --- a/WebCore/platform/graphics/android/TilesManager.h +++ b/WebCore/platform/graphics/android/TilesManager.h @@ -44,6 +44,12 @@ class TileSet; class TilesManager { public: static TilesManager* instance(); + static GLint getMaxTextureSize(); + + static bool hardwareAccelerationEnabled() + { + return gInstance != 0; + } void removeOperationsForPage(TiledPage* page) { @@ -83,14 +89,23 @@ public: } void printTextures(); - void enableTextures(); void resetTextureUsage(TiledPage* page); - void paintTexturesDefault(); - static int maxTextureCount(); + int maxLayersAllocation(); + int maxLayerAllocation(); + int maxTextureCount(); + void setMaxTextureCount(int max); static float tileWidth(); static float tileHeight(); + int expandedTileBoundsX(); + int expandedTileBoundsY(); + + void allocateTiles(); + + void setExpandedTileBounds(bool enabled) { + m_expandedTileBounds = enabled; + } private: @@ -107,8 +122,9 @@ private: Vector<LayerTexture*> m_layersTextures; unsigned int m_layersMemoryUsage; - GLint m_maxTextureSize; - unsigned int m_totalMaxTextureSize; + + int m_maxTextureCount; + bool m_expandedTileBounds; bool m_generatorReady; diff --git a/WebCore/rendering/RenderBox.cpp b/WebCore/rendering/RenderBox.cpp index fe78fd5..ebd7d54 100644 --- a/WebCore/rendering/RenderBox.cpp +++ b/WebCore/rendering/RenderBox.cpp @@ -39,7 +39,9 @@ #include "FloatQuad.h" #include "Frame.h" #include "Page.h" +#if PLATFORM(ANDROID) #include "PlatformBridge.h" +#endif #include "RenderArena.h" #include "RenderFlexibleBox.h" #include "RenderInline.h" @@ -2078,13 +2080,14 @@ void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const { +#if PLATFORM(ANDROID) // Fixed element's position should be decided by the visible screen size. // That is in the doc coordindate. if (style()->position() == FixedPosition && containingBlock->isRenderView()) { const RenderView* view = toRenderView(containingBlock); - return PlatformBridge::visibleScreenWidth(view->frameView()); + return PlatformBridge::screenWidthInDocCoord(view->frameView()); } - +#endif if (containingBlock->isBox()) { const RenderBox* containingBlockBox = toRenderBox(containingBlock); return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth(); @@ -2114,14 +2117,15 @@ int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* con } int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const -{ +{ +#if PLATFORM(ANDROID) // Fixed element's position should be decided by the visible screen size. // That is in the doc coordindate. if (style()->position() == FixedPosition && containingBlock->isRenderView()) { const RenderView* view = toRenderView(containingBlock); - return PlatformBridge::visibleScreenHeight(view->frameView()); + return PlatformBridge::screenHeightInDocCoord(view->frameView()); } - +#endif int heightResult = 0; if (containingBlock->isBox()) heightResult = toRenderBox(containingBlock)->height(); diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 1b1273e..559f25c 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -2180,7 +2180,8 @@ RenderLayer::updateScrollInfoAfterLayout() m_hasOverflowScroll = hasOverflowScroll; dirtyZOrderLists(); dirtyStackingContextZOrderLists(); - renderer()->node()->setNeedsStyleRecalc(SyntheticStyleChange); + if (renderer()->node()) + renderer()->node()->setNeedsStyleRecalc(SyntheticStyleChange); } #endif } diff --git a/WebCore/rendering/RenderTheme.cpp b/WebCore/rendering/RenderTheme.cpp index 522bd4d..538b6c6 100644 --- a/WebCore/rendering/RenderTheme.cpp +++ b/WebCore/rendering/RenderTheme.cpp @@ -198,7 +198,7 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El return adjustTextFieldStyle(selector, style, e); case TextAreaPart: return adjustTextAreaStyle(selector, style, e); -#ifdef ANDROID_LISTBOX_USES_MENU_LIST +#if ENABLE(NO_LISTBOX_RENDERING) case ListboxPart: return adjustListboxStyle(selector, style, e); #endif diff --git a/WebCore/rendering/RenderTheme.h b/WebCore/rendering/RenderTheme.h index aedb8eb..13c69e6 100644 --- a/WebCore/rendering/RenderTheme.h +++ b/WebCore/rendering/RenderTheme.h @@ -236,7 +236,7 @@ protected: virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&) { return true; } -#ifdef ANDROID_LISTBOX_USES_MENU_LIST +#if ENABLE(NO_LISTBOX_RENDERING) virtual void adjustListboxStyle(CSSStyleSelector*, RenderStyle*, Element*) const {} #endif virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp index f0958d9..fb5701a 100644 --- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp @@ -536,7 +536,7 @@ bool ChromeClientAndroid::selectItemWritingDirectionIsNatural() PassRefPtr<PopupMenu> ChromeClientAndroid::createPopupMenu(PopupMenuClient* client) const { - return adoptRef(new PopupMenuAndroid(client)); + return adoptRef(new PopupMenuAndroid(static_cast<ListPopupMenuClient*>(client))); } PassRefPtr<SearchPopupMenu> ChromeClientAndroid::createSearchPopupMenu(PopupMenuClient*) const diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp index cc21dad..535c0da 100644 --- a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp @@ -577,6 +577,12 @@ void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, (m_frame->loader()->policyChecker()->*func)(PolicyUse); } +void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form) +{ + if (m_webFrame->shouldSaveFormData()) + m_webFrame->saveFormData(form); +} + void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) { notImplemented(); } diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h index 964ac6e..034333e 100644 --- a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h +++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h @@ -223,7 +223,7 @@ namespace android { void enableOnDemandPlugins() { m_onDemandPluginsEnabled = true; } void dispatchDidChangeIcons(); - void dispatchWillSendSubmitEvent(HTMLFormElement*) { } + void dispatchWillSendSubmitEvent(HTMLFormElement*); private: CacheBuilder m_cacheBuilder; Frame* m_frame; diff --git a/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/WebKit/android/WebCoreSupport/PlatformBridge.cpp index b34ff8c..8d8d809 100644 --- a/WebKit/android/WebCoreSupport/PlatformBridge.cpp +++ b/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -160,16 +160,16 @@ FloatRect PlatformBridge::screenRect() } // The visible size on screen in document coordinate -int PlatformBridge::visibleScreenWidth(const WebCore::FrameView* frameView) +int PlatformBridge::screenWidthInDocCoord(const WebCore::FrameView* frameView) { android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); - return webViewCore->visibleScreenWidth(); + return webViewCore->screenWidth(); } -int PlatformBridge::visibleScreenHeight(const WebCore::FrameView* frameView) +int PlatformBridge::screenHeightInDocCoord(const WebCore::FrameView* frameView) { android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); - return webViewCore->visibleScreenHeight(); + return webViewCore->screenHeight(); } String PlatformBridge::computeDefaultLanguage() diff --git a/WebKit/android/WebCoreSupport/WebCookieJar.cpp b/WebKit/android/WebCoreSupport/WebCookieJar.cpp index d290b5a..9c1d7fa 100644 --- a/WebKit/android/WebCoreSupport/WebCookieJar.cpp +++ b/WebKit/android/WebCoreSupport/WebCookieJar.cpp @@ -37,6 +37,8 @@ namespace android { static WTF::Mutex instanceMutex; +static bool isFirstInstanceCreated = false; +static bool fileSchemeCookiesEnabled = false; static const std::string& databaseDirectory() { @@ -99,6 +101,9 @@ scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing) WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing) { MutexLocker lock(instanceMutex); + if (!isFirstInstanceCreated && fileSchemeCookiesEnabled) + net::CookieMonster::EnableFileScheme(); + isFirstInstanceCreated = true; scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing); if (!instancePtr->get()) *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing)); @@ -117,9 +122,6 @@ void WebCookieJar::cleanup(bool isPrivateBrowsing) WebCookieJar::WebCookieJar(const std::string& databaseFilePath) : m_allowCookies(true) { - // This is needed for the page cycler. See http://b/2944150 - net::CookieMonster::EnableFileScheme(); - // Setup the permissions for the file const char* cDatabasePath = databaseFilePath.c_str(); mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; @@ -130,6 +132,7 @@ WebCookieJar::WebCookieJar(const std::string& databaseFilePath) if (fd >= 0) close(fd); } + FilePath cookiePath(databaseFilePath.c_str()); m_cookieDb = new SQLitePersistentCookieStore(cookiePath); m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0); @@ -227,4 +230,22 @@ void WebCookieJar::flush() semaphore->Wait(2); } +bool WebCookieJar::acceptFileSchemeCookies() +{ + MutexLocker lock(instanceMutex); + return fileSchemeCookiesEnabled; +} + +void WebCookieJar::setAcceptFileSchemeCookies(bool accept) +{ + // The Chromium HTTP stack only reflects changes to this flag when creating + // a new CookieMonster instance. While we could track whether any + // CookieMonster instances currently exist, this would be complicated and is + // not required, so we only allow this flag to be changed before the first + // instance is created. + MutexLocker lock(instanceMutex); + if (!isFirstInstanceCreated) + fileSchemeCookiesEnabled = accept; +} + } diff --git a/WebKit/android/WebCoreSupport/WebCookieJar.h b/WebKit/android/WebCoreSupport/WebCookieJar.h index 2f32e1a..1f4266c 100644 --- a/WebKit/android/WebCoreSupport/WebCookieJar.h +++ b/WebKit/android/WebCoreSupport/WebCookieJar.h @@ -49,6 +49,12 @@ public: bool allowCookies(); void setAllowCookies(bool allow); + // Getter and setter for whether we accept cookies for file scheme URLS. + // Defaults to false. Note that calls to the setter are ignored once the + // first instance of this class has been created. + static bool acceptFileSchemeCookies(); + static void setAcceptFileSchemeCookies(bool); + // Instead of this it would probably be better to add the cookie methods // here so the rest of WebKit doesn't have to know about Chromium classes net::CookieStore* cookieStore() { return m_cookieStore.get(); } diff --git a/WebKit/android/WebCoreSupport/WebRequest.cpp b/WebKit/android/WebCoreSupport/WebRequest.cpp index e44b600..a14036f 100644 --- a/WebKit/android/WebCoreSupport/WebRequest.cpp +++ b/WebKit/android/WebCoreSupport/WebRequest.cpp @@ -473,7 +473,7 @@ void WebRequest::startReading() if (!read(&bytesRead)) { if (m_request && m_request->status().is_io_pending()) return; // Wait for OnReadCompleted() - finish(false); + return finish(false); } // bytesRead == 0 indicates finished diff --git a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp index 5c54000..642a81a 100644 --- a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp +++ b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp @@ -38,7 +38,6 @@ #include "WebResourceRequest.h" #include <wtf/text/CString.h> -#include <sstream> namespace android { @@ -88,6 +87,8 @@ bool WebUrlLoaderClient::isActive() const { if (m_cancelling) return false; + if (!m_resourceHandle) + return false; if (!m_resourceHandle->client()) return false; if (m_finished) @@ -344,30 +345,6 @@ void WebUrlLoaderClient::maybeCallOnMainThread(Task* task) } } -namespace { -// Convert a CertPrincipal into string readable by Java code. -// The expected format is "CN=xxx, O=xxx, OU=xxx" (see SslCertificate.DName). -// If there are multiple organization names, we print them all. -static std::string certPrincipalToString(const net::CertPrincipal& cp) -{ - std::string result; - if (!cp.common_name.empty()) { - result += "CN="; - result += cp.common_name; - } - std::vector<std::string>::const_iterator i; - for (i = cp.organization_names.begin(); i != cp.organization_names.end(); ++i) { - result += result.empty() ? "O=" : ", O="; - result += *i; - } - for (i = cp.organization_unit_names.begin(); i != cp.organization_unit_names.end(); ++i) { - result += result.empty() ? "OU=" : ", OU="; - result += *i; - } - return result; -} -} - // Response methods void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse) { @@ -379,13 +356,11 @@ void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse) if (m_isMainResource) { // If we got an SSL certificate, tell the WebView about it. - const net::SSLInfo& ssl = m_response->getSslInfo(); - if (ssl.cert) { - m_webFrame->setCertificate( - certPrincipalToString(ssl.cert->subject()), - certPrincipalToString(ssl.cert->issuer()), - 1000L * ssl.cert->valid_start().ToDoubleT(), - 1000L * ssl.cert->valid_expiry().ToDoubleT()); + const net::SSLInfo& ssl_info = m_response->getSslInfo(); + if (ssl_info.is_valid()) { + std::vector<std::string> chain_bytes; + ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes); + m_webFrame->setCertificate(chain_bytes[0]); } } } diff --git a/WebKit/android/jni/CookieManager.cpp b/WebKit/android/jni/CookieManager.cpp index 87c7fa8..a9c68fd 100644 --- a/WebKit/android/jni/CookieManager.cpp +++ b/WebKit/android/jni/CookieManager.cpp @@ -155,6 +155,25 @@ static void flushCookieStore(JNIEnv*, jobject) #endif } +static bool acceptFileSchemeCookies(JNIEnv*, jobject) +{ +#if USE(CHROME_NETWORK_STACK) + return WebCookieJar::acceptFileSchemeCookies(); +#else + // File scheme cookies are always accepted with the Android HTTP stack. + return true; +#endif +} + +static void setAcceptFileSchemeCookies(JNIEnv*, jobject, jboolean accept) +{ +#if USE(CHROME_NETWORK_STACK) + WebCookieJar::setAcceptFileSchemeCookies(accept); +#else + // File scheme cookies are always accepted with the Android HTTP stack. +#endif +} + static JNINativeMethod gCookieManagerMethods[] = { { "nativeAcceptCookie", "()Z", (void*) acceptCookie }, { "nativeGetCookie", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getCookie }, @@ -165,6 +184,8 @@ static JNINativeMethod gCookieManagerMethods[] = { { "nativeSetAcceptCookie", "(Z)V", (void*) setAcceptCookie }, { "nativeSetCookie", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) setCookie }, { "nativeFlushCookieStore", "()V", (void*) flushCookieStore }, + { "nativeAcceptFileSchemeCookies", "()Z", (void*) acceptFileSchemeCookies }, + { "nativeSetAcceptFileSchemeCookies", "(Z)V", (void*) setAcceptFileSchemeCookies }, }; int registerCookieManager(JNIEnv* env) diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp index 9780d2d..c187d92 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -44,7 +44,6 @@ #include "Element.h" #include "FocusController.h" #include "Font.h" -#include "FormState.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClientAndroid.h" @@ -220,6 +219,8 @@ struct WebFrame::JavaBrowserFrame jmethodID mDidReceiveData; jmethodID mDidFinishLoading; jmethodID mSetCertificate; + jmethodID mShouldSaveFormData; + jmethodID mSaveFormData; AutoJObject frame(JNIEnv* env) { return getRealObject(env, mObj); } @@ -290,8 +291,9 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V"); mJavaFrame->mDidReceiveData = env->GetMethodID(clazz, "didReceiveData", "([BI)V"); mJavaFrame->mDidFinishLoading = env->GetMethodID(clazz, "didFinishLoading", "()V"); - mJavaFrame->mSetCertificate = env->GetMethodID(clazz, "setCertificate", - "(Ljava/lang/String;Ljava/lang/String;JJ)V"); + mJavaFrame->mSetCertificate = env->GetMethodID(clazz, "setCertificate", "([B)V"); + mJavaFrame->mShouldSaveFormData = env->GetMethodID(clazz, "shouldSaveFormData", "()Z"); + mJavaFrame->mSaveFormData = env->GetMethodID(clazz, "saveFormData", "(Ljava/util/HashMap;)V"); env->DeleteLocalRef(clazz); LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource"); @@ -322,6 +324,8 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* LOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData"); LOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading"); LOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate"); + LOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData"); + LOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData"); mUserAgent = WTF::String(); mUserInitiatedAction = false; @@ -782,6 +786,16 @@ WebFrame::canHandleRequest(const WebCore::ResourceRequest& request) return (ret == 0); } +bool +WebFrame::shouldSaveFormData() +{ + JNIEnv* env = getJNIEnv(); + jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mShouldSaveFormData); + checkException(env); + return ret; +} + WebCore::Frame* WebFrame::createWindow(bool dialog, bool userGesture) { @@ -950,20 +964,21 @@ WebFrame::didFinishLoading() { #endif #if USE(CHROME_NETWORK_STACK) -void WebFrame::setCertificate(const std::string& issuedTo, const std::string& issuedBy, long long validNotBeforeMillis, long long validNotAfterMillis) +void WebFrame::setCertificate(const std::string& cert) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); #endif JNIEnv* env = getJNIEnv(); - jstring jIssuedTo = stdStringToJstring(env, issuedTo, true); - jstring jIssuedBy = stdStringToJstring(env, issuedBy, true); - env->CallVoidMethod(mJavaFrame->frame(env).get(), - mJavaFrame->mSetCertificate, jIssuedTo, jIssuedBy, validNotBeforeMillis, validNotAfterMillis); + int len = cert.length(); + jbyteArray jCert = env->NewByteArray(len); + jbyte* bytes = env->GetByteArrayElements(jCert, NULL); + cert.copy(reinterpret_cast<char*>(bytes), len); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetCertificate, jCert); - env->DeleteLocalRef(jIssuedTo); - env->DeleteLocalRef(jIssuedBy); + env->DeleteLocalRef(jCert); checkException(env); } #endif @@ -1876,62 +1891,46 @@ static void SetUsernamePassword(JNIEnv *env, jobject obj, } } -static jobject GetFormTextData(JNIEnv *env, jobject obj) +void +WebFrame::saveFormData(HTMLFormElement* form) { -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); -#endif - WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); - LOG_ASSERT(pFrame, "GetFormTextData must take a valid frame pointer!"); - jobject hashMap = NULL; - - WTF::PassRefPtr<WebCore::HTMLCollection> collection = pFrame->document()->forms(); - if (collection->length() > 0) { + if (form->autoComplete()) { + JNIEnv* env = getJNIEnv(); jclass mapClass = env->FindClass("java/util/HashMap"); LOG_ASSERT(mapClass, "Could not find HashMap class!"); jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V"); LOG_ASSERT(init, "Could not find constructor for HashMap"); - hashMap = env->NewObject(mapClass, init, 1); + jobject hashMap = env->NewObject(mapClass, init, 1); LOG_ASSERT(hashMap, "Could not create a new HashMap"); jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); LOG_ASSERT(put, "Could not find put method on HashMap"); - - WebCore::HTMLFormElement* form; - WebCore::HTMLInputElement* input; - for (WebCore::Node* node = collection->firstItem(); - node && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty(); - node = collection->nextItem()) { - form = static_cast<WebCore::HTMLFormElement*>(node); - if (form->autoComplete()) { - WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->associatedElements(); - size_t size = elements.size(); - for (size_t i = 0; i < size; i++) { - WebCore::HTMLFormControlElement* e = elements[i]; - if (e->hasTagName(WebCore::HTMLNames::inputTag)) { - input = static_cast<WebCore::HTMLInputElement*>(e); - if (input->isTextField() && !input->isPasswordField() - && input->autoComplete()) { - WTF::String value = input->value(); - int len = value.length(); - if (len) { - const WTF::AtomicString& name = input->name(); - jstring key = wtfStringToJstring(env, name); - jstring val = wtfStringToJstring(env, value); - LOG_ASSERT(key && val, "name or value not set"); - env->CallObjectMethod(hashMap, put, key, val); - env->DeleteLocalRef(key); - env->DeleteLocalRef(val); - } - } + WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i < size; i++) { + WebCore::HTMLFormControlElement* e = elements[i]; + if (e->hasTagName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(e); + if (input->isTextField() && !input->isPasswordField() + && input->autoComplete()) { + WTF::String value = input->value(); + int len = value.length(); + if (len) { + const WTF::AtomicString& name = input->name(); + jstring key = wtfStringToJstring(env, name); + jstring val = wtfStringToJstring(env, value); + LOG_ASSERT(key && val, "name or value not set"); + env->CallObjectMethod(hashMap, put, key, val); + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); } } } } + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSaveFormData, hashMap); + env->DeleteLocalRef(hashMap); env->DeleteLocalRef(mapClass); - } - return hashMap; } static void OrientationChanged(JNIEnv *env, jobject obj, int orientation) @@ -2048,8 +2047,6 @@ static JNINativeMethod gBrowserFrameNativeMethods[] = { (void*) GetUsernamePassword }, { "setUsernamePassword", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) SetUsernamePassword }, - { "getFormTextData", "()Ljava/util/HashMap;", - (void*) GetFormTextData }, { "nativeOrientationChanged", "(I)V", (void*) OrientationChanged }, { "nativeAuthenticationProceed", "(ILjava/lang/String;Ljava/lang/String;)V", diff --git a/WebKit/android/jni/WebCoreFrameBridge.h b/WebKit/android/jni/WebCoreFrameBridge.h index ae62835..af7be60 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.h +++ b/WebKit/android/jni/WebCoreFrameBridge.h @@ -37,6 +37,7 @@ #include <wtf/RefCounted.h> namespace WebCore { + class HTMLFormElement; class Frame; class HistoryItem; class Image; @@ -125,18 +126,15 @@ class WebFrame : public WebCoreRefObject { void maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request); - void setCertificate(const std::string& issuedTo, const std::string& issuedBy, long long validNotBeforeMillis, long long validNotAfterMillis); + void setCertificate(const std::string& cert); /** - * When the user initiates an action (via trackball, key-press, or touch), - * we set mUserInitiatedAction to true. If a load happens due to this click, - * then we ask the application if it wants to override - * the load. Otherwise, we attempt to load the resource internally. + * When the user initiates a click, we set mUserInitiatedAction to true. + * If a load happens due to this click, then we ask the application if it wants + * to override the load. Otherwise, we attempt to load the resource internally. */ void setUserInitiatedAction(bool userInitiatedAction) { mUserInitiatedAction = userInitiatedAction; } - bool userInitiatedAction() { return mUserInitiatedAction; } - WebCore::Page* page() const { return mPage; } // Currently used only by the chrome net stack. A similar field is used by @@ -151,6 +149,8 @@ class WebFrame : public WebCoreRefObject { bool getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password); jbyteArray getPostData(const WebCore::ResourceRequest& request); + bool shouldSaveFormData(); + void saveFormData(WebCore::HTMLFormElement*); private: struct JavaBrowserFrame; JavaBrowserFrame* mJavaFrame; diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp index 8361c17..8c0fade 100644 --- a/WebKit/android/jni/WebViewCore.cpp +++ b/WebKit/android/jni/WebViewCore.cpp @@ -35,6 +35,8 @@ #include "Chrome.h" #include "ChromeClientAndroid.h" #include "ChromiumIncludes.h" +#include "ClientRect.h" +#include "ClientRectList.h" #include "Color.h" #include "CSSPropertyNames.h" #include "CSSValueKeywords.h" @@ -250,9 +252,7 @@ struct WebViewCoreFields { struct WebViewCore::JavaGlue { jweak m_obj; - jmethodID m_spawnScrollTo; jmethodID m_scrollTo; - jmethodID m_scrollBy; jmethodID m_contentDraw; jmethodID m_layersDraw; jmethodID m_requestListBox; @@ -342,15 +342,14 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m #endif m_isPaused = false; m_screenOnCounter = 0; + m_onlyScrollIfImeIsShowing = false; LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!"); jclass clazz = env->GetObjectClass(javaWebViewCore); m_javaGlue = new JavaGlue; m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore); - m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V"); - m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V"); - m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V"); + m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V"); m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V"); m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V"); m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V"); @@ -486,8 +485,6 @@ void WebViewCore::reset(bool fromConstructor) m_scrollOffsetY = 0; m_screenWidth = 0; m_screenHeight = 0; - m_visibleScreenWidth = 0; - m_visibleScreenHeight = 0; m_groupForVisitedLinks = NULL; m_currentNodeDomNavigationAxis = 0; } @@ -947,9 +944,8 @@ void WebViewCore::scrollTo(int x, int y, bool animate) // LOGD("WebViewCore::scrollTo(%d %d)\n", x, y); JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo, - x, y); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollTo, + x, y, animate, m_onlyScrollIfImeIsShowing); checkException(env); } @@ -971,16 +967,6 @@ void WebViewCore::viewInvalidate(const WebCore::IntRect& rect) checkException(env); } -void WebViewCore::scrollBy(int dx, int dy, bool animate) -{ - if (!(dx | dy)) - return; - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy, - dx, dy, animate); - checkException(env); -} - void WebViewCore::contentDraw() { JNIEnv* env = JSC::Bindings::getJNIEnv(); @@ -1151,13 +1137,14 @@ void WebViewCore::doMaxScroll(CacheBuilder::Direction dir) default: LOG_ASSERT(0, "unexpected focus selector"); } - this->scrollBy(dx, dy, true); + WebCore::FrameView* view = m_mainFrame->view(); + this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true); } -void WebViewCore::setScrollOffset(int moveGeneration, int userScrolled, int dx, int dy) +void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy) { - DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), userScrolled=%d", dx, dy, - m_scrollOffsetX, m_scrollOffsetY, userScrolled); + DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy, + m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent); if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) { m_scrollOffsetX = dx; m_scrollOffsetY = dy; @@ -1166,24 +1153,25 @@ void WebViewCore::setScrollOffset(int moveGeneration, int userScrolled, int dx, // testing work correctly. m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX, m_scrollOffsetY); - if (userScrolled) { + if (sendScrollEvent) { m_mainFrame->eventHandler()->sendScrollEvent(); - } - // Update history item to reflect the new scroll position. - // This also helps save the history information when the browser goes to - // background, so scroll position will be restored if browser gets - // killed while in background. - WebCore::HistoryController* history = m_mainFrame->loader()->history(); - // Because the history item saving could be heavy for large sites and - // scrolling can generate lots of small scroll offset, the following code - // reduces the saving frequency. - static const int MIN_SCROLL_DIFF = 32; - if (history->currentItem()) { - WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); - if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || - std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { - history->saveScrollPositionAndViewStateToItem(history->currentItem()); + // Only update history position if it's user scrolled. + // Update history item to reflect the new scroll position. + // This also helps save the history information when the browser goes to + // background, so scroll position will be restored if browser gets + // killed while in background. + WebCore::HistoryController* history = m_mainFrame->loader()->history(); + // Because the history item saving could be heavy for large sites and + // scrolling can generate lots of small scroll offset, the following code + // reduces the saving frequency. + static const int MIN_SCROLL_DIFF = 32; + if (history->currentItem()) { + WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); + if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || + std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { + history->saveScrollPositionAndViewStateToItem(history->currentItem()); + } } } @@ -1229,6 +1217,12 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, // Don't reflow if the diff is small. const bool reflow = otw && textWrapWidth && ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f; + + // When the screen size change, fixed positioned element should be updated. + // This is supposed to be light weighted operation without a full layout. + if (osh != screenHeight || osw != screenWidth) + m_mainFrame->view()->updatePositionedObjects(); + if (ow != width || (!ignoreHeight && oh != height) || reflow) { WebCore::RenderObject *r = m_mainFrame->contentRenderer(); DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r, @@ -1322,8 +1316,11 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, // If this was in response to touching a textfield and showing the IME, // the IME may now cover textfield. Bring it back into view. // If the scale changed, however, this was the result of a zoom. - if (oldScale == m_scale) + if (oldScale == m_scale && osh > screenHeight) { + m_onlyScrollIfImeIsShowing = true; revealSelection(); + m_onlyScrollIfImeIsShowing = false; + } // update the currently visible screen as perceived by the plugin sendPluginVisibleScreen(); } @@ -2084,18 +2081,18 @@ String WebViewCore::modifySelection(const int direction, const int axis) if (selection->rangeCount() > 1) selection->removeAllRanges(); switch (axis) { - case AXIS_CHARACTER: - case AXIS_WORD: - case AXIS_SENTENCE: - return modifySelectionTextNavigationAxis(selection, direction, axis); - case AXIS_HEADING: - case AXIS_SIBLING: - case AXIS_PARENT_FIRST_CHILD: - case AXIS_DOCUMENT: - return modifySelectionDomNavigationAxis(selection, direction, axis); - default: - LOGE("Invalid navigation axis: %d", axis); - return String(); + case AXIS_CHARACTER: + case AXIS_WORD: + case AXIS_SENTENCE: + return modifySelectionTextNavigationAxis(selection, direction, axis); + case AXIS_HEADING: + case AXIS_SIBLING: + case AXIS_PARENT_FIRST_CHILD: + case AXIS_DOCUMENT: + return modifySelectionDomNavigationAxis(selection, direction, axis); + default: + LOGE("Invalid navigation axis: %d", axis); + return String(); } } @@ -2123,10 +2120,10 @@ void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis) { - // TODO: Add support of IFrames. - HTMLElement* body = m_mainFrame->document()->body(); + Node* body = m_mainFrame->document()->body(); ExceptionCode ec = 0; + String markup; // initialize the selection if necessary if (selection->rangeCount() == 0) { @@ -2151,14 +2148,13 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i if (ec) return String(); selection->addRange(rangeRef.get()); - } else if (direction == DIRECTION_FORWARD) { - selection->setPosition(body->firstDescendant(), 0, ec); } else { - selection->setPosition(body->lastDescendant(), 0, ec); + selection->setPosition(body, 0, ec); } if (ec) return String(); } + // collapse the selection if (direction == DIRECTION_FORWARD) selection->collapseToEnd(ec); @@ -2167,126 +2163,52 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i if (ec) return String(); - Node* oldAnchorNode = selection->anchorNode(); - if (!oldAnchorNode) - return String(); - // Make sure the anchor node is a text node since we are generating // the markup of the selection which includes the anchor, the focus, // and any crossed nodes. Forcing the condition that the selection // starts and ends on text nodes guarantees symmetric selection markup. + // Also this way the text content, rather its container, is highlighted. Node* anchorNode = selection->anchorNode(); if (anchorNode->isElementNode()) { - int anchorOffset = rangeCompliantChildOffset(anchorNode, - selection->anchorOffset()); - anchorNode = selection->anchorNode()->childNodes()->item(anchorOffset); - Node* nextAnchorNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - anchorNode, body, direction); - if (!nextAnchorNode) + // Collapsed selection while moving forward points to the + // next unvisited node and while moving backward to the + // last visited node. + if (direction == DIRECTION_FORWARD) + advanceAnchorNode(selection, direction, markup, false, ec); + else + advanceAnchorNode(selection, direction, markup, true, ec); + if (ec) return String(); - if (direction == DIRECTION_FORWARD) { - Node* skippedControl = getFirstIntermediaryInputOrButton(anchorNode, - nextAnchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMinOffset(skippedControl), skippedControl, - caretMaxOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, 0, ec); - if (ec) - return String(); - } - } else { - Node* skippedControl = getFirstIntermediaryInputOrButton( - nextAnchorNode, anchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMaxOffset(skippedControl), skippedControl, - caretMinOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, - caretMaxOffset(nextAnchorNode), ec); - if (ec) - return String(); - } - } + if (!markup.isEmpty()) + return markup; } // If the selection is at the end of a non white space text move // it to the next visible text node with non white space content. // This is a workaround for the selection getting stuck. anchorNode = selection->anchorNode(); - if (!anchorNode) - return String(); if (anchorNode->isTextNode()) { if (direction == DIRECTION_FORWARD) { String suffix = anchorNode->textContent().substring( - selection->anchorOffset(), caretMaxOffset(anchorNode)); + selection->anchorOffset(), caretMaxOffset(anchorNode)); + // If at the end of non white space text we advance the + // anchor node to either an input element or non empty text. if (suffix.stripWhiteSpace().isEmpty()) { - Node* nextAnchorNode = - traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, - body, direction); - if (!nextAnchorNode) - return String(); - Node* skippedControl = getFirstIntermediaryInputOrButton( - anchorNode, nextAnchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMinOffset(skippedControl), skippedControl, - caretMaxOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, 0, ec); - if (ec) - return String(); - } + advanceAnchorNode(selection, direction, markup, true, ec); } } else { String prefix = anchorNode->textContent().substring(0, - selection->anchorOffset()); + selection->anchorOffset()); + // If at the end of non white space text we advance the + // anchor node to either an input element or non empty text. if (prefix.stripWhiteSpace().isEmpty()) { - Node* nextAnchorNode = - traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, - body, direction); - if (!nextAnchorNode) - return String(); - Node* skippedControl = getFirstIntermediaryInputOrButton( - nextAnchorNode, anchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMaxOffset(skippedControl), skippedControl, - caretMinOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, - caretMaxOffset(nextAnchorNode), ec); - if (ec) - return String(); - } + advanceAnchorNode(selection, direction, markup, true, ec); } } + if (ec) + return String(); + if (!markup.isEmpty()) + return markup; } // extend the selection @@ -2308,176 +2230,111 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i // Make sure the focus node is a text node in order to have the // selection generate symmetric markup because the latter - // includes all nodes crossed by the selection. + // includes all nodes crossed by the selection. Also this way + // the text content, rather its container, is highlighted. Node* focusNode = selection->focusNode(); if (focusNode->isElementNode()) { - int focusOffset = rangeCompliantChildOffset(focusNode, - selection->focusOffset()); + focusNode = getImplicitAnchorOrFocusNode(selection->focusNode(), + selection->focusOffset(), direction); + if (!focusNode) + return String(); if (direction == DIRECTION_FORWARD) { - focusNode = - focusNode->childNodes()->item(focusOffset)->lastDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, body, DIRECTION_BACKWARD); - if (!focusNode) + focusNode = focusNode->traversePreviousSiblingPostOrder(body); + if (focusNode && !isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_BACKWARD); + if (textNode) + anchorNode = textNode; + } + if (focusNode && isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMaxOffset(focusNode), ec); + if (ec) return String(); } - selection->extend(focusNode, caretMaxOffset(focusNode), ec); - if (ec) - return String(); } else { - focusNode = - focusNode->childNodes()->item(focusOffset)->firstDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, body, DIRECTION_FORWARD); - if (!focusNode) + focusNode = focusNode->traverseNextSibling(); + if (focusNode && !isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_FORWARD); + if (textNode) + anchorNode = textNode; + } + if (anchorNode && isContentTextNode(anchorNode)) { + selection->extend(focusNode, 0, ec); + if (ec) return String(); } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); } } // Enforce that the selection does not cross anchor boundaries. This is // a workaround for the asymmetric behavior of WebKit while crossing // anchors. - // NOTE: The code is asymmetric since the logic is based off the common - // ancestor in both directions - backward and forward. - // TODO: Factor out common code repeated below. - anchorNode = selection->anchorNode(); - focusNode = selection->focusNode(); - if (anchorNode != focusNode - && anchorNode->isTextNode() - && focusNode->isTextNode()) { - Node* commonAncestor = Range::commonAncestorContainer(anchorNode, - focusNode); - Node* currentNode = 0; - bool selectionAdjusted = false; - if (direction == DIRECTION_FORWARD) { - // catch if the anchor is in a link but the focus is not - if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { - currentNode = anchorNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) && isInputControl(currentNode)) { - focusNode = currentNode->lastDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_BACKWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, caretMaxOffset(focusNode), - ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->parentNode(); + anchorNode = getImplicitAnchorOrFocusNode(selection->anchorNode(), + selection->anchorOffset(), direction); + focusNode = getImplicitAnchorOrFocusNode(selection->focusNode(), + selection->focusOffset(), direction); + if (anchorNode && focusNode && anchorNode != focusNode) { + Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode, + direction); + if (inputControl) { + if (direction == DIRECTION_FORWARD) { + if (isDescendantOf(inputControl, anchorNode)) { + focusNode = inputControl; + } else { + focusNode = inputControl->traversePreviousSiblingPostOrder( + body); + if (!focusNode) + focusNode = inputControl; } - // catch if there is a link between the anchor and focus - if (!selectionAdjusted) { - currentNode = anchorNode; - while (currentNode != focusNode) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode; - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_BACKWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, - caretMaxOffset(focusNode), ec); - if (ec) - return String(); - break; - } - currentNode = currentNode->traverseNextNode(); - } + // We prefer a text node contained in the input element. + if (!isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_BACKWARD); + if (textNode) + focusNode = textNode; } - } - } else { - // catch if the anchor is in a link but the focus is not - // NOTE: There is not such case in forward direction because - // it is implicitly covered the second case. Also the - // base position used for computing the the common - // ancestor which is asymmteric. - if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { - currentNode = anchorNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) && isInputControl(currentNode)) { - focusNode = currentNode->firstDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->parentNode(); + // If we found text in the input select it. + // Otherwise, select the input element itself. + if (isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMaxOffset(focusNode), ec); + } else if (anchorNode != focusNode) { + // Note that the focusNode always has parent and that + // the offset can be one more that the index of the last + // element - this is how WebKit selects such elements. + selection->extend(focusNode->parentNode(), + focusNode->nodeIndex() + 1, ec); } - // catch if there is a link between the anchor and focus - if (!selectionAdjusted) { - currentNode = anchorNode; - while (currentNode != focusNode) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode->traverseNextSibling(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->traversePreviousNode(); - } + if (ec) + return String(); + } else { + if (isDescendantOf(inputControl, anchorNode)) { + focusNode = inputControl; + } else { + focusNode = inputControl->traverseNextSibling(); + if (!focusNode) + focusNode = inputControl; } - // catch if the focus is in a link but the anchor is not - if (!selectionAdjusted) { - currentNode = focusNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode->traverseNextSibling(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - break; - } - currentNode = currentNode->parentNode(); - } + // We prefer a text node contained in the input element. + if (!isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_FORWARD); + if (textNode) + focusNode = textNode; + } + // If we found text in the input select it. + // Otherwise, select the input element itself. + if (isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMinOffset(focusNode), ec); + } else if (anchorNode != focusNode) { + // Note that the focusNode always has parent and that + // the offset can be one more that the index of the last + // element - this is how WebKit selects such elements. + selection->extend(focusNode->parentNode(), + focusNode->nodeIndex() + 1, ec); } + if (ec) + return String(); } } } @@ -2494,30 +2351,136 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i return String(); IntRect bounds = range->boundingBox(); selectAt(bounds.center().x(), bounds.center().y()); - String markup = formatMarkup(selection).stripWhiteSpace(); + markup = formatMarkup(selection); LOGV("Selection markup: %s", markup.utf8().data()); return markup; } -bool WebViewCore::isInputControl(Node* node) +Node* WebViewCore::getImplicitAnchorOrFocusNode(Node* node, unsigned offset, int direction) { - return (node->hasTagName(WebCore::HTMLNames::aTag) - || node->hasTagName(WebCore::HTMLNames::inputTag) - || node->hasTagName(WebCore::HTMLNames::buttonTag)); + if (node->offsetInCharacters()) + return node; + if (!node->hasChildNodes()) + return node; + if (offset < node->childNodeCount()) + return node->childNode(offset); + else + if (direction == DIRECTION_FORWARD) + return node->traverseNextSibling(); + else + return node->traversePreviousNodePostOrder( + node->document()->body()); } -int WebViewCore::rangeCompliantChildOffset(Node* parent, int offset) +Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction) { - if (offset < 0) - return 0; - int lastChildIndex = parent->childNodes()->length() - 1; - if (offset > lastChildIndex) - return lastChildIndex; - return offset; + Node* body = 0; + Node* currentNode = 0; + if (direction == DIRECTION_FORWARD) { + if (ignoreFirstNode) + currentNode = anchorNode->traverseNextNode(body); + else + currentNode = anchorNode; + } else { + body = anchorNode->document()->body(); + if (ignoreFirstNode) + currentNode = anchorNode->traversePreviousSiblingPostOrder(body); + else + currentNode = anchorNode; + } + while (currentNode) { + if (isContentTextNode(currentNode) + || isContentInputElement(currentNode)) + return currentNode; + if (direction == DIRECTION_FORWARD) + currentNode = currentNode->traverseNextNode(); + else + currentNode = currentNode->traversePreviousNodePostOrder(body); + } + return 0; +} + +void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction, + String& markup, bool ignoreFirstNode, ExceptionCode& ec) +{ + Node* anchorNode = getImplicitAnchorOrFocusNode(selection->anchorNode(), + selection->anchorOffset(), direction); + if (!anchorNode) { + ec = NOT_FOUND_ERR; + return; + } + // If the anchor offset is invalid i.e. the anchor node has no + // child with that index getImplicitAnchorNode returns the next + // logical node in the current direction. In such a case our + // position in the DOM tree was has already been advanced, + // therefore we there is no need to do that again. + if (selection->anchorNode()->isElementNode()) { + unsigned anchorOffset = selection->anchorOffset(); + unsigned childNodeCount = selection->anchorNode()->childNodeCount(); + if (anchorOffset >= childNodeCount) + ignoreFirstNode = false; + } + // Find the next anchor node given our position in the DOM and + // whether we want the current node to be considered as well. + Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode, + direction); + if (!nextAnchorNode) { + ec = NOT_FOUND_ERR; + return; + } + if (nextAnchorNode->isElementNode()) { + // If this is an input element tell the WebView thread + // to set the cursor to that control. + if (isContentInputElement(nextAnchorNode)) { + IntRect bounds = nextAnchorNode->getRect(); + selectAt(bounds.center().x(), bounds.center().y()); + } + Node* textNode = 0; + // Treat the text content of links as any other text but + // for the rest input elements select the control itself. + if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag)) + textNode = traverseNextContentTextNode(nextAnchorNode, + nextAnchorNode, direction); + // We prefer to select the text content of the link if such, + // otherwise just select the element itself. + if (textNode) { + nextAnchorNode = textNode; + } else { + if (direction == DIRECTION_FORWARD) { + selection->setBaseAndExtent(nextAnchorNode, + caretMinOffset(nextAnchorNode), nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); + } else { + selection->setBaseAndExtent(nextAnchorNode, + caretMaxOffset(nextAnchorNode), nextAnchorNode, + caretMinOffset(nextAnchorNode), ec); + } + if (!ec) + markup = formatMarkup(selection); + // make sure the selection is visible + scrollNodeIntoView(selection->frame(), nextAnchorNode); + return; + } + } + if (direction == DIRECTION_FORWARD) + selection->setPosition(nextAnchorNode, + caretMinOffset(nextAnchorNode), ec); + else + selection->setPosition(nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); +} + +bool WebViewCore::isContentInputElement(Node* node) +{ + return (isVisible(node) + && (node->hasTagName(WebCore::HTMLNames::selectTag) + || node->hasTagName(WebCore::HTMLNames::aTag) + || node->hasTagName(WebCore::HTMLNames::inputTag) + || node->hasTagName(WebCore::HTMLNames::buttonTag))); } -bool WebViewCore::isVisibleNonEmptyNonWhitespaceTextNode(Node* node) +bool WebViewCore::isContentTextNode(Node* node) { if (!node || !node->isTextNode()) return false; @@ -2526,21 +2489,66 @@ bool WebViewCore::isVisibleNonEmptyNonWhitespaceTextNode(Node* node) && !textNode->containsOnlyWhitespace()); } -Text* WebViewCore::traverseVisibleNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode, int direction) +Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction) { Node* currentNode = fromNode; do { if (direction == DIRECTION_FORWARD) currentNode = currentNode->traverseNextNode(toNode); else - currentNode = currentNode->traversePreviousNode(toNode); - } while (currentNode && !isVisibleNonEmptyNonWhitespaceTextNode(currentNode)); + currentNode = currentNode->traversePreviousNodePostOrder(toNode); + } while (currentNode && !isContentTextNode(currentNode)); return static_cast<Text*>(currentNode); } +Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction) +{ + if (fromNode == toNode) + return 0; + if (direction == DIRECTION_FORWARD) { + Node* currentNode = fromNode; + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traverseNextNodePostOrder(); + } + currentNode = fromNode; + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traverseNextNode(); + } + } else { + Node* currentNode = fromNode->traversePreviousNode(); + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traversePreviousNode(); + } + currentNode = fromNode->traversePreviousNodePostOrder(); + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traversePreviousNodePostOrder(); + } + } + return 0; +} + +bool WebViewCore::isDescendantOf(Node* parent, Node* node) +{ + Node* currentNode = node; + while (currentNode) { + if (currentNode == parent) { + return true; + } + currentNode = currentNode->parentNode(); + } + return false; +} + String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis) { - // TODO: Add support of IFrames. HTMLElement* body = m_mainFrame->document()->body(); if (!m_currentNodeDomNavigationAxis && selection->focusNode()) { m_currentNodeDomNavigationAxis = selection->focusNode(); @@ -2633,12 +2641,27 @@ bool WebViewCore::isHeading(Node* node) bool WebViewCore::isVisible(Node* node) { - // TODO: Use DOMWindow#getComputedStyle instead. + // start off an element + Element* element = 0; + if (node->isElementNode()) + element = static_cast<Element*>(node); + else + element = node->parentElement(); + // check renderer + if (!element->renderer()) { + return false; + } + // check size + if (element->offsetHeight() == 0 || element->offsetWidth() == 0) { + return false; + } + // check style Node* body = m_mainFrame->document()->body(); - Node* currentNode = node; + Node* currentNode = element; while (currentNode && currentNode != body) { RenderStyle* style = currentNode->computedStyle(); - if (style->display() == NONE || style->visibility() == HIDDEN) { + if (style && + (style->display() == NONE || style->visibility() == HIDDEN)) { return false; } currentNode = currentNode->parentNode(); @@ -2650,20 +2673,18 @@ String WebViewCore::formatMarkup(DOMSelection* selection) { ExceptionCode ec = 0; String markup = String(); - PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec); if (ec) - return markup; - + return String(); if (!wholeRange->startContainer() || !wholeRange->startContainer()) - return markup; - + return String(); // Since formatted markup contains invisible nodes it // is created from the concatenation of the visible fragments. Node* firstNode = wholeRange->firstNode(); Node* pastLastNode = wholeRange->pastLastNode(); Node* currentNode = firstNode; PassRefPtr<Range> currentRange; + while (currentNode != pastLastNode) { Node* nextNode = currentNode->traverseNextNode(); if (!isVisible(currentNode)) { @@ -2676,24 +2697,24 @@ String WebViewCore::formatMarkup(DOMSelection* selection) if (!currentRange) { currentRange = selection->frame()->document()->createRange(); if (ec) - return markup; + break; if (currentNode == firstNode) { currentRange->setStart(wholeRange->startContainer(), wholeRange->startOffset(), ec); if (ec) - return markup; + break; } else { currentRange->setStart(currentNode->parentNode(), currentNode->nodeIndex(), ec); if (ec) - return markup; + break; } } if (nextNode == pastLastNode) { currentRange->setEnd(wholeRange->endContainer(), wholeRange->endOffset(), ec); if (ec) - return markup; + break; markup = markup + stripAppleSpanFromMarkup( currentRange->toHTML()).utf8().data(); } else { @@ -2704,12 +2725,12 @@ String WebViewCore::formatMarkup(DOMSelection* selection) currentRange->setEnd(currentNode->parentNode(), currentNode->nodeIndex() + 1, ec); if (ec) - return markup; + break; } } currentNode = nextNode; } - return markup; + return markup.stripWhiteSpace(); } String WebViewCore::stripAppleSpanFromMarkup(String markup) @@ -2732,32 +2753,6 @@ void WebViewCore::selectAt(int x, int y) checkException(env); } -Node* WebViewCore::getFirstIntermediaryInputOrButton(Node* fromNode, Node* toNode) -{ - // do bidirectional traversal to catch the case in which - // the toNode is a descendant of a control but the fromNode - // is not and the other way around - Node* currentNode = fromNode->traverseNextNode(); - while (currentNode && currentNode != toNode) { - if (isVisible(currentNode) - && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) - || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { - return currentNode; - } - currentNode = currentNode->traverseNextNode(); - } - currentNode = fromNode->traverseNextNodePostOrder(); - while (currentNode && currentNode != toNode) { - if (isVisible(currentNode) - && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) - || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { - return currentNode; - } - currentNode = currentNode->traverseNextNodePostOrder(); - } - return 0; -} - void WebViewCore::deleteSelection(int start, int end, int textGeneration) { setSelection(start, end); @@ -2882,94 +2877,6 @@ void WebViewCore::saveDocumentState(WebCore::Frame* frame) } } -// Convert a WTF::String into an array of characters where the first -// character represents the length, for easy conversion to java. -static uint16_t* stringConverter(const WTF::String& text) -{ - size_t length = text.length(); - uint16_t* itemName = new uint16_t[length+1]; - itemName[0] = (uint16_t)length; - uint16_t* firstChar = &(itemName[1]); - memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length); - return itemName; -} - -// Response to dropdown created for a listbox. -class ListBoxReply : public WebCoreReply { -public: - ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view) - : m_select(select) - , m_frame(frame) - , m_viewImpl(view) - {} - - // Response used for a multiple selection listbox if the user did not change - // anything, in which case -2 is used. - // Also used by a listbox which has single selection but a size is set. - virtual void replyInt(int index) - { - if (-2 == index) { - // Special value for cancel. Do nothing. - return; - } - // If the select element no longer exists, due to a page change, etc, - // silently return. - if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame, - m_frame, m_select)) - return; - // Use a pointer to HTMLSelectElement's superclass, where - // listToOptionIndex is public - SelectElement* selectElement = m_select; - int optionIndex = selectElement->listToOptionIndex(index); - m_select->setSelectedIndex(optionIndex, true); - m_select->dispatchFormControlChangeEvent(); - m_viewImpl->contentInvalidate(m_select->getRect()); - } - - // Response if the listbox allows multiple selection. array stores the listIndices - // of selected positions. - virtual void replyIntArray(const int* array, int count) - { - // If the select element no longer exists, due to a page change, etc, - // silently return. - if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame, - m_frame, m_select)) - return; - - const WTF::Vector<Element*>& items = m_select->listItems(); - int totalItems = static_cast<int>(items.size()); - // Keep track of the position of the value we are comparing against. - int arrayIndex = 0; - // The value we are comparing against. - int selection = array[arrayIndex]; - WebCore::HTMLOptionElement* option; - for (int listIndex = 0; listIndex < totalItems; listIndex++) { - if (items[listIndex]->hasLocalName(WebCore::HTMLNames::optionTag)) { - option = static_cast<WebCore::HTMLOptionElement*>( - items[listIndex]); - if (listIndex == selection) { - option->setSelectedState(true); - arrayIndex++; - if (arrayIndex == count) - selection = -1; - else - selection = array[arrayIndex]; - } else - option->setSelectedState(false); - } - } - m_select->dispatchFormControlChangeEvent(); - m_viewImpl->contentInvalidate(m_select->getRect()); - } -private: - // The select element associated with this listbox. - WebCore::HTMLSelectElement* m_select; - // The frame of this select element, to verify that it is valid. - WebCore::Frame* m_frame; - // For calling invalidate and checking the select element's validity - WebViewCore* m_viewImpl; -}; - // Create an array of java Strings. static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count) { @@ -3284,46 +3191,7 @@ bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* node return true; } - WebCore::RenderObject* renderer = nodePtr->renderer(); - if (renderer && renderer->isListBox()) { - WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr); - const WTF::Vector<WebCore::Element*>& listItems = select->listItems(); - SkTDArray<const uint16_t*> names; - // Possible values for enabledArray. Keep in Sync with values in - // InvokeListBox.Container in WebView.java - enum OptionStatus { - OPTGROUP = -1, - OPTION_DISABLED = 0, - OPTION_ENABLED = 1, - }; - SkTDArray<int> enabledArray; - SkTDArray<int> selectedArray; - int size = listItems.size(); - bool multiple = select->multiple(); - for (int i = 0; i < size; i++) { - if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) { - WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]); - *names.append() = stringConverter(option->textIndentedToRespectGroupLabel()); - *enabledArray.append() = option->disabled() ? OPTION_DISABLED : OPTION_ENABLED; - if (multiple && option->selected()) - *selectedArray.append() = i; - } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) { - WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]); - *names.append() = stringConverter(optGroup->groupLabelText()); - *enabledArray.append() = OPTGROUP; - } - } - WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this); - // Use a pointer to HTMLSelectElement's superclass, where - // optionToListIndex is public. - SelectElement* selectElement = select; - listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(), - multiple, selectedArray.begin(), multiple ? selectedArray.count() : - selectElement->optionToListIndex(select->selectedIndex())); - DBG_NAV_LOG("list box"); - return true; - } - scrollLayer(renderer, &m_mousePos); + scrollLayer(nodePtr->renderer(), &m_mousePos); } if (!valid || !framePtr) framePtr = m_mainFrame; @@ -3789,10 +3657,12 @@ void WebViewCore::notifyWebAppCanBeInstalled() void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary) { +#if ENABLE(WEB_AUTOFILL) JNIEnv* env = JSC::Bindings::getJNIEnv(); jstring preview = env->NewString(previewSummary.data(), previewSummary.length()); env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview); env->DeleteLocalRef(preview); +#endif } bool WebViewCore::drawIsPaused() const @@ -3875,7 +3745,7 @@ static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, screenWidth, screenHeight, anchorX, anchorY, ignoreHeight); } -static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint userScrolled, jint x, jint y) +static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); @@ -3883,7 +3753,7 @@ static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint userScrolle WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); LOG_ASSERT(viewImpl, "need viewImpl"); - viewImpl->setScrollOffset(gen, userScrolled, x, y); + viewImpl->setScrollOffset(gen, sendScrollEvent, x, y); } static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h, @@ -4535,7 +4405,7 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) SendListBoxChoice }, { "nativeSetSize", "(IIIFIIIIZ)V", (void*) SetSize }, - { "nativeSetScrollOffset", "(IIII)V", + { "nativeSetScrollOffset", "(IZII)V", (void*) SetScrollOffset }, { "nativeSetGlobalBounds", "(IIII)V", (void*) SetGlobalBounds }, diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h index 1457089..d38be79 100644 --- a/WebKit/android/jni/WebViewCore.h +++ b/WebKit/android/jni/WebViewCore.h @@ -149,14 +149,6 @@ namespace android { void scrollTo(int x, int y, bool animate = false); /** - * Scroll to the point x,y relative to the current position. - * @param x The relative x position. - * @param y The relative y position. - * @param animate If it is true, animate to the new scroll position - */ - void scrollBy(int x, int y, bool animate); - - /** * Record the invalid rectangle */ void contentInvalidate(const WebCore::IntRect &rect); @@ -311,7 +303,7 @@ namespace android { WebCore::Frame* frame, int x, int y); // set the scroll amount that webview.java is currently showing - void setScrollOffset(int moveGeneration, int userScrolled, int dx, int dy); + void setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy); void setGlobalBounds(int x, int y, int h, int v); @@ -579,10 +571,9 @@ namespace android { bool isPaused() const { return m_isPaused; } void setIsPaused(bool isPaused) { m_isPaused = isPaused; } bool drawIsPaused() const; - int visibleScreenWidth() const { return m_visibleScreenWidth; } - int visibleScreenHeight() const { return m_visibleScreenHeight; } - void setVisibleScreenWidth(int w) { m_visibleScreenWidth = w; } - void setVisibleScreenHeight(int h) { m_visibleScreenHeight = h; } + // The actual content (without title bar) size in doc coordinate + int screenWidth() const { return m_screenWidth; } + int screenHeight() const { return m_screenHeight; } #if USE(CHROME_NETWORK_STACK) void setWebRequestContextUserAgent(); void setWebRequestContextCacheMode(int mode); @@ -610,6 +601,9 @@ namespace android { int m_blurringNodePointer; int m_lastFocusedSelStart; int m_lastFocusedSelEnd; + // Pass along with a scroll message to tell the UI thread to only + // scroll the page if the IME is showing. + bool m_onlyScrollIfImeIsShowing; PictureSet m_content; // the set of pictures to draw SkRegion m_addInval; // the accumulated inval region (not yet drawn) SkRegion m_rebuildInval; // the accumulated region for rebuilt pictures @@ -635,11 +629,6 @@ namespace android { CachedHistory m_history; int m_screenWidth; // width of the visible rect in document coordinates int m_screenHeight;// height of the visible rect in document coordinates - // The m_screenHeight is not equal to the visibleRect from WebView, - // using m_visibleScreenHeight to store that info. - // After we can fix the m_screenHeight in java side, we can merge them. - int m_visibleScreenWidth; - int m_visibleScreenHeight; int m_textWrapWidth; float m_scale; unsigned m_domtree_version; @@ -674,18 +663,21 @@ namespace android { // below are members responsible for accessibility support String modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int granularity); String modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int granularity); - Text* traverseVisibleNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode ,int direction); + Text* traverseNextContentTextNode(Node* fromNode, Node* toNode ,int direction); bool isVisible(Node* node); bool isHeading(Node* node); String formatMarkup(DOMSelection* selection); void selectAt(int x, int y); Node* m_currentNodeDomNavigationAxis; void scrollNodeIntoView(Frame* frame, Node* node); - bool isVisibleNonEmptyNonWhitespaceTextNode(Node* node); + bool isContentTextNode(Node* node); String stripAppleSpanFromMarkup(String markup); - int rangeCompliantChildOffset(Node* parent, int offset); - Node* getFirstIntermediaryInputOrButton(Node* fromNode, Node* toNode); - bool isInputControl(Node* node); + Node* getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction); + bool isContentInputElement(Node* node); + bool isDescendantOf(Node* parent, Node* node); + void advanceAnchorNode(DOMSelection* selection, int direction, String& markup, bool ignoreFirstNode, ExceptionCode& ec); + Node* getNextAnchorNode(Node* anchorNode, bool skipFirstHack, int direction); + Node* getImplicitAnchorOrFocusNode(Node* node, unsigned offset, int direction); #if ENABLE(TOUCH_EVENTS) bool m_forwardingTouchEvents; diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp index 27eebd3..b25ad7d 100644 --- a/WebKit/android/nav/CachedFrame.cpp +++ b/WebKit/android/nav/CachedFrame.cpp @@ -50,13 +50,17 @@ WebCore::IntRect CachedFrame::adjustBounds(const CachedNode* node, mRoot->mViewBounds.x(), mRoot->mViewBounds.y(), mRoot->mViewBounds.width(), mRoot->mViewBounds.height()); #if USE(ACCELERATED_COMPOSITING) - if (mRoot) { - const CachedLayer* cachedLayer = layer(node); - const WebCore::LayerAndroid* rootLayer = mRoot->rootLayer(); - const LayerAndroid* aLayer = cachedLayer->layer(rootLayer); - if (aLayer) - return cachedLayer->adjustBounds(rootLayer, rect); - } + if (!mRoot) + return rect; + + const CachedLayer* cachedLayer = layer(node); + if (!cachedLayer) + return rect; + + const WebCore::LayerAndroid* rootLayer = mRoot->rootLayer(); + const LayerAndroid* aLayer = cachedLayer->layer(rootLayer); + if (aLayer) + return cachedLayer->adjustBounds(rootLayer, rect); #endif return rect; } diff --git a/WebKit/android/nav/CachedLayer.cpp b/WebKit/android/nav/CachedLayer.cpp index c8220b3..d5385bd 100644 --- a/WebKit/android/nav/CachedLayer.cpp +++ b/WebKit/android/nav/CachedLayer.cpp @@ -177,7 +177,6 @@ void CachedLayer::Debug::print() const { CachedLayer* b = base(); DUMP_NAV_LOGD(" // int mCachedNodeIndex=%d;\n", b->mCachedNodeIndex); - DUMP_NAV_LOGD(" // LayerAndroid* mLayer=%p;\n", b->mLayer); DUMP_NAV_LOGD(" // int mOffset=(%d, %d);\n", b->mOffset.x(), b->mOffset.y()); DUMP_NAV_LOGD(" // int mUniqueId=%p;\n", b->mUniqueId); diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index f84f8b0..948ea6b 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -206,7 +206,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : // We must remove the m_glWebViewState prior to deleting m_baseLayer. If we // do not remove it here, we risk having BaseTiles trying to paint using a // deallocated base layer. - delete m_glWebViewState; + stopGL(); #endif delete m_frameCacheUI; delete m_navPictureUI; @@ -214,6 +214,14 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : delete m_glDrawFunctor; } +void stopGL() +{ +#if USE(ACCELERATED_COMPOSITING) + delete m_glWebViewState; + m_glWebViewState = 0; +#endif +} + WebViewCore* getWebViewCore() const { return m_viewImpl; } @@ -481,9 +489,7 @@ bool drawGL(WebCore::IntRect& viewRect, float scale, int extras) SkRect visibleRect; calcOurContentVisibleRect(&visibleRect); - m_viewImpl->setVisibleScreenWidth(visibleRect.width()); - m_viewImpl->setVisibleScreenHeight(visibleRect.height()); - bool ret = m_baseLayer->drawGL(viewRect, visibleRect, scale); + bool ret = m_glWebViewState->drawGL(viewRect, visibleRect, scale); if (ret || m_glWebViewState->currentPictureCounter() != pic) return true; #endif @@ -545,8 +551,6 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) compositeLayer->setExtra(extra); SkRect visible; calcOurContentVisibleRect(&visible); - m_viewImpl->setVisibleScreenWidth(visible.width()); - m_viewImpl->setVisibleScreenHeight(visible.height()); // call this to be sure we've adjusted for any scrolling or animations // before we actually draw compositeLayer->updateFixedLayersPositions(visible); @@ -2207,6 +2211,11 @@ static void nativeDestroy(JNIEnv *env, jobject obj) delete view; } +static void nativeStopGL(JNIEnv *env, jobject obj) +{ + GET_NATIVE_VIEW(env, obj)->stopGL(); +} + static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj) { WebView* view = GET_NATIVE_VIEW(env, obj); @@ -2395,6 +2404,11 @@ static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint layerId, jint x, return false; } +static void nativeSetExpandedTileBounds(JNIEnv*, jobject, jboolean enabled) +{ + TilesManager::instance()->setExpandedTileBounds(enabled); +} + /* * JNI registration */ @@ -2559,6 +2573,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeShowCursorTimed }, { "nativeStartSelection", "(II)Z", (void*) nativeStartSelection }, + { "nativeStopGL", "()V", + (void*) nativeStopGL }, { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;", (void*) nativeSubtractLayers }, { "nativeTextGeneration", "()I", @@ -2573,6 +2589,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeScrollableLayer }, { "nativeScrollLayer", "(III)Z", (void*) nativeScrollLayer }, + { "nativeSetExpandedTileBounds", "(Z)V", + (void*) nativeSetExpandedTileBounds }, }; int registerWebView(JNIEnv* env) diff --git a/WebKit/android/plugins/ANPOpenGLInterface.cpp b/WebKit/android/plugins/ANPOpenGLInterface.cpp index 8f5f9b4..015a04c 100644 --- a/WebKit/android/plugins/ANPOpenGLInterface.cpp +++ b/WebKit/android/plugins/ANPOpenGLInterface.cpp @@ -87,6 +87,9 @@ static void anp_releaseTexture(NPP instance, const ANPTextureInfo* textureInfo) info->m_internalFormat = textureInfo->internalFormat; texture->producerReleaseAndSwap(); + + // invalidate the java view so that this content is drawn + pluginWidget->viewInvalidate(); } static void anp_invertPluginContent(NPP instance, bool isContentInverted) { diff --git a/WebKit/android/plugins/PluginWidgetAndroid.cpp b/WebKit/android/plugins/PluginWidgetAndroid.cpp index 4dc12a3..06506ba 100644 --- a/WebKit/android/plugins/PluginWidgetAndroid.cpp +++ b/WebKit/android/plugins/PluginWidgetAndroid.cpp @@ -132,7 +132,19 @@ void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) { m_pluginBounds.fRight, m_pluginBounds.fBottom); const bool boundsChanged = m_pluginBounds != oldPluginBounds; - sendSizeAndVisibilityEvents(boundsChanged); + + //TODO hack to ensure that we grab the most recent screen dimensions and scale + ANPRectI screenCoords; + m_core->getVisibleScreen(screenCoords); + float scale = m_core->scale(); + bool scaleChanged = m_cachedZoomLevel != scale; + setVisibleScreen(screenCoords, scale); + + // if the scale changed then setVisibleScreen will call this function and + // this call will potentially fire a duplicate draw event + if (!scaleChanged) { + sendSizeAndVisibilityEvents(boundsChanged); + } layoutSurface(boundsChanged); if (m_drawingModel != kSurface_ANPDrawingModel) { @@ -144,8 +156,14 @@ void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) { bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) { - if (model == kOpenGL_ANPDrawingModel && m_layer == 0) - m_layer = new WebCore::MediaLayer(); + if (model == kOpenGL_ANPDrawingModel && m_layer == 0) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jobject webview = m_core->getWebViewJavaObject(); + jobject weakWebViewRef = 0; + if (webview) + weakWebViewRef = env->NewWeakGlobalRef(webview); + m_layer = new WebCore::MediaLayer(weakWebViewRef); + } else if (model != kOpenGL_ANPDrawingModel && m_layer != 0) m_layer->unref(); @@ -192,6 +210,12 @@ void PluginWidgetAndroid::inval(const WebCore::IntRect& rect, } } +void PluginWidgetAndroid::viewInvalidate() { + WebCore::IntRect rect(m_pluginBounds.fLeft, m_pluginBounds.fTop, + m_pluginBounds.width(), m_pluginBounds.height()); + m_core->viewInvalidate(rect); +} + void PluginWidgetAndroid::draw(SkCanvas* canvas) { if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) { return; @@ -563,7 +587,7 @@ void PluginWidgetAndroid::scrollToVisiblePluginRect() { #if DEBUG_VISIBLE_RECTS PLUGIN_LOG("%s call scrollBy (%d,%d)", __FUNCTION__, deltaX, deltaY); #endif - core->scrollBy(deltaX, deltaY, true); + core->scrollTo(rectCenterX, rectCenterY, true); } void PluginWidgetAndroid::requestFullScreen() { diff --git a/WebKit/android/plugins/PluginWidgetAndroid.h b/WebKit/android/plugins/PluginWidgetAndroid.h index 9726a22..5d586b1 100644 --- a/WebKit/android/plugins/PluginWidgetAndroid.h +++ b/WebKit/android/plugins/PluginWidgetAndroid.h @@ -177,6 +177,8 @@ struct PluginWidgetAndroid { void setPowerState(ANPPowerState powerState); + void viewInvalidate(); + private: void computeVisiblePluginRect(); void scrollToVisiblePluginRect(); |