/* * Copyright 2007, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "webviewglue" #include "config.h" #include "AndroidAnimation.h" #include "AndroidLog.h" #include "BaseLayerAndroid.h" #include "BaseRenderer.h" #include "DrawExtra.h" #include "DumpLayer.h" #include "Frame.h" #include "GLWebViewState.h" #include "GraphicsJNI.h" #include "HTMLInputElement.h" #include "IntPoint.h" #include "IntRect.h" #include "LayerAndroid.h" #include "LayerContent.h" #include "Node.h" #include "utils/Functor.h" #include "private/hwui/DrawGlInfo.h" #include "PlatformGraphicsContext.h" #include "PlatformString.h" #include "ScrollableLayerAndroid.h" #include "SelectText.h" #include "SkCanvas.h" #include "SkDumpCanvas.h" #include "SkPicture.h" #include "SkRect.h" #include "SkTime.h" #include "TilesManager.h" #include "TransferQueue.h" #include "WebCoreJni.h" #include "WebRequestContext.h" #include "WebViewCore.h" #ifdef GET_NATIVE_VIEW #undef GET_NATIVE_VIEW #endif #define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField)) #include #include #include #include #include #include // Free as much as we possible can #define TRIM_MEMORY_COMPLETE 80 // Free a lot (all textures gone) #define TRIM_MEMORY_MODERATE 60 // More moderate free (keep bare minimum to restore quickly-ish - possibly clear all textures) #define TRIM_MEMORY_BACKGROUND 40 // Moderate free (clear cached tiles, keep visible ones) #define TRIM_MEMORY_UI_HIDDEN 20 // Duration to show the pressed cursor ring #define PRESSED_STATE_DURATION 400 namespace android { static jfieldID gWebViewField; //------------------------------------- static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[]) { jmethodID m = env->GetMethodID(clazz, name, signature); ALOG_ASSERT(m, "Could not find method %s", name); return m; } //------------------------------------- // This class provides JNI for making calls into native code from the UI side // of the multi-threaded WebView. class WebView { public: enum FrameCachePermission { DontAllowNewer, AllowNewer }; #define DRAW_EXTRAS_SIZE 2 enum DrawExtras { // keep this in sync with WebView.java DrawExtrasNone = 0, DrawExtrasSelection = 1, DrawExtrasCursorRing = 2 }; struct JavaGlue { jweak m_obj; jmethodID m_scrollBy; jmethodID m_getScaledMaxXScroll; jmethodID m_getScaledMaxYScroll; jmethodID m_updateRectsForGL; jmethodID m_viewInvalidate; jmethodID m_viewInvalidateRect; jmethodID m_postInvalidateDelayed; jmethodID m_pageSwapCallback; jfieldID m_rectLeft; jfieldID m_rectTop; jmethodID m_rectWidth; jmethodID m_rectHeight; jfieldID m_quadFP1; jfieldID m_quadFP2; jfieldID m_quadFP3; jfieldID m_quadFP4; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } } m_javaGlue; WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, bool isHighEndGfx) : m_isHighEndGfx(isHighEndGfx) { memset(m_extras, 0, DRAW_EXTRAS_SIZE * sizeof(DrawExtra*)); jclass clazz = env->FindClass("android/webkit/WebViewClassic"); m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView); m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z"); m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I"); m_javaGlue.m_getScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I"); m_javaGlue.m_updateRectsForGL = GetJMethod(env, clazz, "updateRectsForGL", "()V"); m_javaGlue.m_viewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V"); m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V"); m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz, "viewInvalidateDelayed", "(JIIII)V"); m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "(Z)V"); env->DeleteLocalRef(clazz); jclass rectClass = env->FindClass("android/graphics/Rect"); ALOG_ASSERT(rectClass, "Could not find Rect class"); m_javaGlue.m_rectLeft = env->GetFieldID(rectClass, "left", "I"); m_javaGlue.m_rectTop = env->GetFieldID(rectClass, "top", "I"); m_javaGlue.m_rectWidth = GetJMethod(env, rectClass, "width", "()I"); m_javaGlue.m_rectHeight = GetJMethod(env, rectClass, "height", "()I"); env->DeleteLocalRef(rectClass); jclass quadFClass = env->FindClass("android/webkit/QuadF"); ALOG_ASSERT(quadFClass, "Could not find QuadF class"); m_javaGlue.m_quadFP1 = env->GetFieldID(quadFClass, "p1", "Landroid/graphics/PointF;"); m_javaGlue.m_quadFP2 = env->GetFieldID(quadFClass, "p2", "Landroid/graphics/PointF;"); m_javaGlue.m_quadFP3 = env->GetFieldID(quadFClass, "p3", "Landroid/graphics/PointF;"); m_javaGlue.m_quadFP4 = env->GetFieldID(quadFClass, "p4", "Landroid/graphics/PointF;"); env->DeleteLocalRef(quadFClass); env->SetIntField(javaWebView, gWebViewField, (jint)this); m_viewImpl = (WebViewCore*) viewImpl; m_generation = 0; m_heightCanMeasure = false; m_lastDx = 0; m_lastDxTime = 0; m_baseLayer = 0; m_glDrawFunctor = 0; m_isDrawingPaused = false; #if USE(ACCELERATED_COMPOSITING) m_glWebViewState = 0; #endif } ~WebView() { if (m_javaGlue.m_obj) { JNIEnv* env = JSC::Bindings::getJNIEnv(); env->DeleteWeakGlobalRef(m_javaGlue.m_obj); m_javaGlue.m_obj = 0; } #if USE(ACCELERATED_COMPOSITING) // 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. stopGL(); #endif SkSafeUnref(m_baseLayer); delete m_glDrawFunctor; for (int i = 0; i < DRAW_EXTRAS_SIZE; i++) delete m_extras[i]; } DrawExtra* getDrawExtra(DrawExtras extras) { if (extras == DrawExtrasNone) return 0; return m_extras[extras - 1]; } void stopGL() { #if USE(ACCELERATED_COMPOSITING) delete m_glWebViewState; m_glWebViewState = 0; #endif } WebViewCore* getWebViewCore() const { return m_viewImpl; } void scrollRectOnScreen(const IntRect& rect) { if (rect.isEmpty()) return; int dx = 0; int left = rect.x(); int right = rect.maxX(); if (left < m_visibleContentRect.fLeft) dx = left - m_visibleContentRect.fLeft; // Only scroll right if the entire width can fit on screen. else if (right > m_visibleContentRect.fRight && right - left < m_visibleContentRect.width()) dx = right - m_visibleContentRect.fRight; int dy = 0; int top = rect.y(); int bottom = rect.maxY(); if (top < m_visibleContentRect.fTop) dy = top - m_visibleContentRect.fTop; // Only scroll down if the entire height can fit on screen else if (bottom > m_visibleContentRect.fBottom && bottom - top < m_visibleContentRect.height()) dy = bottom - m_visibleContentRect.fBottom; if ((dx|dy) == 0 || !scrollBy(dx, dy)) return; viewInvalidate(); } int drawGL(WebCore::IntRect& invScreenRect, WebCore::IntRect* invalRect, WebCore::IntRect& screenRect, int titleBarHeight, WebCore::IntRect& screenClip, float scale, int extras, bool shouldDraw) { #if USE(ACCELERATED_COMPOSITING) if (!m_baseLayer) return 0; if (m_viewImpl) m_viewImpl->setPrerenderingEnabled(!m_isDrawingPaused); if (!m_glWebViewState) { TilesManager::instance()->setHighEndGfx(m_isHighEndGfx); m_glWebViewState = new GLWebViewState(); m_glWebViewState->setBaseLayer(m_baseLayer, false, true); } DrawExtra* extra = getDrawExtra((DrawExtras) extras); m_glWebViewState->glExtras()->setDrawExtra(extra); // Make sure we have valid coordinates. We might not have valid coords // if the zoom manager is still initializing. We will be redrawn // once the correct scale is set if (!m_visibleContentRect.isFinite()) return 0; bool treesSwapped = false; bool newTreeHasAnim = false; int ret = m_glWebViewState->drawGL(invScreenRect, m_visibleContentRect, invalRect, screenRect, titleBarHeight, screenClip, scale, &treesSwapped, &newTreeHasAnim, shouldDraw); if (treesSwapped) { ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (javaObject.get()) { env->CallVoidMethod(javaObject.get(), m_javaGlue.m_pageSwapCallback, newTreeHasAnim); checkException(env); } } return m_isDrawingPaused ? 0 : ret; #endif return 0; } void draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras) { if (!m_baseLayer) { canvas->drawColor(bgColor); return; } // draw the content of the base layer first LayerContent* content = m_baseLayer->content(); int sc = canvas->save(SkCanvas::kClip_SaveFlag); if (content) { canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(), content->height()), SkRegion::kDifference_Op); } Color c = m_baseLayer->getBackgroundColor(); canvas->drawColor(SkColorSetARGBInline(c.alpha(), c.red(), c.green(), c.blue())); canvas->restoreToCount(sc); // call this to be sure we've adjusted for any scrolling or animations // before we actually draw m_baseLayer->updatePositionsRecursive(m_visibleContentRect); m_baseLayer->updatePositions(); // We have to set the canvas' matrix on the base layer // (to have fixed layers work as intended) SkAutoCanvasRestore restore(canvas, true); m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); m_baseLayer->draw(canvas, getDrawExtra(extras)); } int getScaledMaxXScroll() { ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return 0; int result = env->CallIntMethod(javaObject.get(), m_javaGlue.m_getScaledMaxXScroll); checkException(env); return result; } int getScaledMaxYScroll() { ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return 0; int result = env->CallIntMethod(javaObject.get(), m_javaGlue.m_getScaledMaxYScroll); checkException(env); return result; } // Call through JNI to ask Java side to update the rectangles for GL functor. // This is called at every draw when it is not in process mode, so we should // keep this route as efficient as possible. Currently, its average cost on Xoom // is about 0.1ms - 0.2ms. // Alternatively, this can be achieved by adding more listener on Java side, but // that will be more likely causing jank when triggering GC. void updateRectsForGL() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_updateRectsForGL); checkException(env); } #if USE(ACCELERATED_COMPOSITING) static const ScrollableLayerAndroid* findScrollableLayer( const LayerAndroid* parent, int x, int y, SkIRect* foundBounds) { IntRect bounds = enclosingIntRect(parent->fullContentAreaMapped()); // Check the parent bounds first; this will clip to within a masking layer's // bounds. if (parent->masksToBounds() && !bounds.contains(x, y)) return 0; int count = parent->countChildren(); while (count--) { const LayerAndroid* child = parent->getChild(count); const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y, foundBounds); if (result) { if (parent->masksToBounds()) { if (bounds.width() < foundBounds->width()) foundBounds->fRight = foundBounds->fLeft + bounds.width(); if (bounds.height() < foundBounds->height()) foundBounds->fBottom = foundBounds->fTop + bounds.height(); } return result; } } if (parent->contentIsScrollable()) { foundBounds->set(bounds.x(), bounds.y(), bounds.width(), bounds.height()); return static_cast(parent); } return 0; } #endif int scrollableLayer(int x, int y, SkIRect* layerRect, SkIRect* bounds) { #if USE(ACCELERATED_COMPOSITING) if (!m_baseLayer) return 0; const ScrollableLayerAndroid* result = findScrollableLayer(m_baseLayer, x, y, bounds); if (result) { result->getScrollRect(layerRect); return result->uniqueId(); } #endif return 0; } void scrollLayer(int layerId, int x, int y) { if (m_glWebViewState) m_glWebViewState->scrollLayer(layerId, x, y); } void setHeightCanMeasure(bool measure) { m_heightCanMeasure = measure; } String getSelection() { SelectText* select = static_cast( getDrawExtra(WebView::DrawExtrasSelection)); if (select) return select->getText(); return String(); } bool scrollBy(int dx, int dy) { ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return false; bool result = env->CallBooleanMethod(javaObject.get(), m_javaGlue.m_scrollBy, dx, dy, true); checkException(env); return result; } void setIsScrolling(bool isScrolling) { #if USE(ACCELERATED_COMPOSITING) if (m_glWebViewState) m_glWebViewState->setIsScrolling(isScrolling); #endif } void viewInvalidate() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_viewInvalidate); checkException(env); } void viewInvalidateRect(int l, int t, int r, int b) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_viewInvalidateRect, l, r, t, b); checkException(env); } void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_postInvalidateDelayed, delay, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY()); checkException(env); } #if ENABLE(ANDROID_OVERFLOW_SCROLL) static void copyScrollPosition(const LayerAndroid* fromRoot, LayerAndroid* toRoot, int layerId) { if (!fromRoot || !toRoot) return; const LayerAndroid* from = fromRoot->findById(layerId); LayerAndroid* to = toRoot->findById(layerId); if (!from || !to || !from->contentIsScrollable() || !to->contentIsScrollable()) return; // TODO: Support this for iframes. if (to->isIFrameContent() || from->isIFrameContent()) return; to->setScrollOffset(from->getScrollOffset()); } #endif BaseLayerAndroid* getBaseLayer() const { return m_baseLayer; } bool setBaseLayer(BaseLayerAndroid* newBaseLayer, bool showVisualIndicator, bool isPictureAfterFirstLayout, int scrollingLayer) { bool queueFull = false; #if USE(ACCELERATED_COMPOSITING) if (m_glWebViewState) queueFull = m_glWebViewState->setBaseLayer(newBaseLayer, showVisualIndicator, isPictureAfterFirstLayout); #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) copyScrollPosition(m_baseLayer, newBaseLayer, scrollingLayer); #endif SkSafeUnref(m_baseLayer); m_baseLayer = newBaseLayer; return queueFull; } void copyBaseContentToPicture(SkPicture* picture) { if (!m_baseLayer || !m_baseLayer->content()) return; LayerContent* content = m_baseLayer->content(); SkCanvas* canvas = picture->beginRecording(content->width(), content->height(), SkPicture::kUsePathBoundsForClip_RecordingFlag); // clear the BaseLayerAndroid's previous matrix (set at each draw) SkMatrix baseMatrix; baseMatrix.reset(); m_baseLayer->setMatrix(baseMatrix); m_baseLayer->draw(canvas, 0); picture->endRecording(); } bool hasContent() { if (!m_baseLayer || !m_baseLayer->content()) return false; return !m_baseLayer->content()->isEmpty(); } void setFunctor(Functor* functor) { delete m_glDrawFunctor; m_glDrawFunctor = functor; } Functor* getFunctor() { return m_glDrawFunctor; } void setVisibleContentRect(SkRect& visibleContentRect) { m_visibleContentRect = visibleContentRect; } void setDrawExtra(DrawExtra *extra, DrawExtras type) { if (type == DrawExtrasNone) return; DrawExtra* old = m_extras[type - 1]; m_extras[type - 1] = extra; if (old != extra) { delete old; } } void setTextSelection(SelectText *selection) { setDrawExtra(selection, DrawExtrasSelection); } const TransformationMatrix* getLayerTransform(int layerId) { if (layerId != -1 && m_baseLayer) { LayerAndroid* layer = m_baseLayer->findById(layerId); // We need to make sure the drawTransform is up to date as this is // called before a draw() or drawGL() if (layer) { m_baseLayer->updatePositionsRecursive(m_visibleContentRect); return layer->drawTransform(); } } return 0; } int getHandleLayerId(SelectText::HandleId handleId, SkIPoint& cursorPoint, FloatQuad& textBounds) { SelectText* selectText = static_cast(getDrawExtra(DrawExtrasSelection)); if (!selectText || !m_baseLayer) return -1; int layerId = selectText->caretLayerId(handleId); IntRect cursorRect = selectText->caretRect(handleId); IntRect textRect = selectText->textRect(handleId); // Rects exclude the last pixel on right/bottom. We want only included pixels. cursorPoint.set(cursorRect.x(), cursorRect.maxY() - 1); textRect.setHeight(std::max(1, textRect.height() - 1)); textRect.setWidth(std::max(1, textRect.width() - 1)); textBounds = FloatQuad(textRect); const TransformationMatrix* transform = getLayerTransform(layerId); if (transform) { // We're overloading the concept of Rect to be just the two // points (bottom-left and top-right. cursorPoint = transform->mapPoint(cursorPoint); textBounds = transform->mapQuad(textBounds); } return layerId; } void mapLayerRect(int layerId, SkIRect& rect) { const TransformationMatrix* transform = getLayerTransform(layerId); if (transform) rect = transform->mapRect(rect); } void floatQuadToQuadF(JNIEnv* env, const FloatQuad& nativeTextQuad, jobject textQuad) { jobject p1 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP1); jobject p2 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP2); jobject p3 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP3); jobject p4 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP4); GraphicsJNI::point_to_jpointf(nativeTextQuad.p1(), env, p1); GraphicsJNI::point_to_jpointf(nativeTextQuad.p2(), env, p2); GraphicsJNI::point_to_jpointf(nativeTextQuad.p3(), env, p3); GraphicsJNI::point_to_jpointf(nativeTextQuad.p4(), env, p4); env->DeleteLocalRef(p1); env->DeleteLocalRef(p2); env->DeleteLocalRef(p3); env->DeleteLocalRef(p4); } // This is called when WebView switches rendering modes in a more permanent fashion // such as when the layer type is set or the view is attached/detached from the window int setHwAccelerated(bool hwAccelerated) { if (!m_glWebViewState) return 0; LayerAndroid* root = m_baseLayer; if (root) return root->setHwAccelerated(hwAccelerated); return 0; } void setDrawingPaused(bool isPaused) { m_isDrawingPaused = isPaused; if (m_viewImpl) m_viewImpl->setPrerenderingEnabled(!isPaused); } // Finds the rectangles within world to the left, right, top, and bottom // of rect and adds them to rects. If no intersection exists, false is returned. static bool findMaskedRects(const FloatRect& world, const FloatRect& rect, Vector& rects) { if (!world.intersects(rect)) return false; // nothing to subtract // left rectangle if (rect.x() > world.x()) rects.append(FloatRect(world.x(), world.y(), rect.x() - world.x(), world.height())); // top rectangle if (rect.y() > world.y()) rects.append(FloatRect(world.x(), world.y(), world.width(), rect.y() - world.y())); // right rectangle if (rect.maxX() < world.maxX()) rects.append(FloatRect(rect.maxX(), world.y(), world.maxX() - rect.maxX(), world.height())); // bottom rectangle if (rect.maxY() < world.maxY()) rects.append(FloatRect(world.x(), rect.maxY(), world.width(), world.maxY() - rect.maxY())); return true; } // Returns false if layerId is a fixed position layer, otherwise // all fixed position layer rectangles are subtracted from those within // rects. Rects will be modified to contain rectangles that don't include // the fixed position layer rectangles. static bool findMaskedRectsForLayer(LayerAndroid* layer, Vector& rects, int layerId) { if (layer->isPositionFixed()) { if (layerId == layer->uniqueId()) return false; FloatRect layerRect = layer->fullContentAreaMapped(); for (int i = rects.size() - 1; i >= 0; i--) if (findMaskedRects(rects[i], layerRect, rects)) rects.remove(i); } int childIndex = 0; while (LayerAndroid* child = layer->getChild(childIndex++)) if (!findMaskedRectsForLayer(child, rects, layerId)) return false; return true; } // Finds the largest rectangle not masked by any fixed layer. void findMaxVisibleRect(int movingLayerId, SkIRect& visibleContentRect) { if (!m_baseLayer) return; FloatRect visibleContentFloatRect(visibleContentRect); m_baseLayer->updatePositionsRecursive(visibleContentFloatRect); Vector rects; rects.append(visibleContentFloatRect); if (findMaskedRectsForLayer(m_baseLayer, rects, movingLayerId)) { float maxSize = 0.0; const FloatRect* largest = 0; for (unsigned int i = 0; i < rects.size(); i++) { const FloatRect& rect = rects[i]; float size = rect.width() * rect.height(); if (size > maxSize) { maxSize = size; largest = ▭ } } if (largest) { SkRect largeRect = *largest; largeRect.round(&visibleContentRect); } } } bool isHandleLeft(SelectText::HandleId handleId) { SelectText* selectText = static_cast(getDrawExtra(DrawExtrasSelection)); if (!selectText) return (handleId == SelectText::BaseHandle); return (selectText->getHandleType(handleId) == SelectText::LeftHandle); } bool isPointVisible(int layerId, int contentX, int contentY) { bool isVisible = true; const TransformationMatrix* transform = getLayerTransform(layerId); if (transform) { // layer is guaranteed to be non-NULL because of getLayerTransform LayerAndroid* layer = m_baseLayer->findById(layerId); IntRect rect = layer->visibleContentArea(); rect = transform->mapRect(rect); isVisible = rect.contains(contentX, contentY); } return isVisible; } private: // local state for WebView bool m_isDrawingPaused; // private to getFrameCache(); other functions operate in a different thread WebViewCore* m_viewImpl; int m_generation; // associate unique ID with sent kit focus to match with ui // Corresponds to the same-named boolean on the java side. bool m_heightCanMeasure; int m_lastDx; SkMSec m_lastDxTime; DrawExtra* m_extras[DRAW_EXTRAS_SIZE]; BaseLayerAndroid* m_baseLayer; Functor* m_glDrawFunctor; #if USE(ACCELERATED_COMPOSITING) GLWebViewState* m_glWebViewState; #endif SkRect m_visibleContentRect; bool m_isHighEndGfx; }; // end of WebView class /** * This class holds a function pointer and parameters for calling drawGL into a specific * viewport. The pointer to the Functor will be put on a framework display list to be called * when the display list is replayed. */ class GLDrawFunctor : Functor { public: GLDrawFunctor(WebView* _wvInstance, int (WebView::*_funcPtr)(WebCore::IntRect&, WebCore::IntRect*, WebCore::IntRect&, int, WebCore::IntRect&, jfloat, jint, bool), WebCore::IntRect _invScreenRect, float _scale, int _extras) { wvInstance = _wvInstance; funcPtr = _funcPtr; invScreenRect = _invScreenRect; scale = _scale; extras = _extras; }; status_t operator()(int messageId, void* data) { TRACE_METHOD(); bool shouldDraw = (messageId == uirenderer::DrawGlInfo::kModeDraw); if (shouldDraw) wvInstance->updateRectsForGL(); if (invScreenRect.isEmpty()) { // NOOP operation if viewport is empty return 0; } WebCore::IntRect inval; int titlebarHeight = screenRect.height() - invScreenRect.height(); uirenderer::DrawGlInfo* info = reinterpret_cast(data); WebCore::IntRect screenClip(info->clipLeft, info->clipTop, info->clipRight - info->clipLeft, info->clipBottom - info->clipTop); WebCore::IntRect localInvScreenRect = invScreenRect; if (info->isLayer) { // When webview is on a layer, we need to use the viewport relative // to the FBO, rather than the screen(which will use invScreenRect). localInvScreenRect.setX(screenClip.x()); localInvScreenRect.setY(info->height - screenClip.y() - screenClip.height()); } // Send the necessary info to the shader. TilesManager::instance()->shader()->setGLDrawInfo(info); int returnFlags = (*wvInstance.*funcPtr)(localInvScreenRect, &inval, screenRect, titlebarHeight, screenClip, scale, extras, shouldDraw); if ((returnFlags & uirenderer::DrawGlInfo::kStatusDraw) != 0) { IntRect finalInval; if (inval.isEmpty()) finalInval = screenRect; else { finalInval.setX(screenRect.x() + inval.x()); finalInval.setY(screenRect.y() + titlebarHeight + inval.y()); finalInval.setWidth(inval.width()); finalInval.setHeight(inval.height()); } info->dirtyLeft = finalInval.x(); info->dirtyTop = finalInval.y(); info->dirtyRight = finalInval.maxX(); info->dirtyBottom = finalInval.maxY(); } // return 1 if invalidation needed, 2 to request non-drawing functor callback, 0 otherwise ALOGV("returnFlags are %d, shouldDraw %d", returnFlags, shouldDraw); return returnFlags; } void updateScreenRect(WebCore::IntRect& _screenRect) { screenRect = _screenRect; } void updateInvScreenRect(WebCore::IntRect& _invScreenRect) { invScreenRect = _invScreenRect; } void updateScale(float _scale) { scale = _scale; } void updateExtras(jint _extras) { extras = _extras; } private: WebView* wvInstance; int (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*, WebCore::IntRect&, int, WebCore::IntRect&, float, int, bool); WebCore::IntRect invScreenRect; WebCore::IntRect screenRect; jfloat scale; jint extras; }; /* * Native JNI methods */ static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl, jstring drawableDir, jboolean isHighEndGfx) { WTF::String dir = jstringToWtfString(env, drawableDir); new WebView(env, obj, viewImpl, dir, isHighEndGfx); // NEED THIS OR SOMETHING LIKE IT! //Release(obj); } static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj) { if (obj) { int L, T, R, B; GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B); return WebCore::IntRect(L, T, R - L, B - T); } else return WebCore::IntRect(); } static SkRect jrectf_to_rect(JNIEnv* env, jobject obj) { SkRect rect = SkRect::MakeEmpty(); if (obj) GraphicsJNI::jrectf_to_rect(env, obj, &rect); return rect; } static void nativeDraw(JNIEnv *env, jobject obj, jobject canv, jobject visible, jint color, jint extras) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); WebView* webView = GET_NATIVE_VIEW(env, obj); SkRect visibleContentRect = jrectf_to_rect(env, visible); webView->setVisibleContentRect(visibleContentRect); webView->draw(canvas, color, static_cast(extras)); } static jint nativeCreateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView, jobject jinvscreenrect, jobject jscreenrect, jobject jvisiblecontentrect, jfloat scale, jint extras) { WebCore::IntRect invScreenRect = jrect_to_webrect(env, jinvscreenrect); WebView *wvInstance = reinterpret_cast(nativeView); SkRect visibleContentRect = jrectf_to_rect(env, jvisiblecontentrect); wvInstance->setVisibleContentRect(visibleContentRect); GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor(); if (!functor) { functor = new GLDrawFunctor(wvInstance, &android::WebView::drawGL, invScreenRect, scale, extras); wvInstance->setFunctor((Functor*) functor); } else { functor->updateInvScreenRect(invScreenRect); functor->updateScale(scale); functor->updateExtras(extras); } WebCore::IntRect rect = jrect_to_webrect(env, jscreenrect); functor->updateScreenRect(rect); return (jint)functor; } static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView) { WebView *wvInstance = reinterpret_cast(nativeView); if (!wvInstance) return 0; return (jint) wvInstance->getFunctor(); } static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView, jobject jinvscreenrect, jobject jscreenrect, jobject jvisiblecontentrect, jfloat scale) { WebView *wvInstance = reinterpret_cast(nativeView); if (wvInstance) { GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor(); if (functor) { WebCore::IntRect invScreenRect = jrect_to_webrect(env, jinvscreenrect); functor->updateInvScreenRect(invScreenRect); SkRect visibleContentRect = jrectf_to_rect(env, jvisiblecontentrect); wvInstance->setVisibleContentRect(visibleContentRect); WebCore::IntRect screenRect = jrect_to_webrect(env, jscreenrect); functor->updateScreenRect(screenRect); functor->updateScale(scale); } } } static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj, jint nativeView) { // only call in software rendering, initialize and evaluate animations #if USE(ACCELERATED_COMPOSITING) BaseLayerAndroid* baseLayer = reinterpret_cast(nativeView)->getBaseLayer(); if (baseLayer) { baseLayer->initAnimations(); return baseLayer->evaluateAnimations(); } #endif return false; } static bool nativeSetBaseLayer(JNIEnv *env, jobject obj, jint nativeView, jint layer, jboolean showVisualIndicator, jboolean isPictureAfterFirstLayout, jint scrollingLayer) { BaseLayerAndroid* layerImpl = reinterpret_cast(layer); return reinterpret_cast(nativeView)->setBaseLayer(layerImpl, showVisualIndicator, isPictureAfterFirstLayout, scrollingLayer); } static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj, jint nativeView) { return reinterpret_cast(nativeView)->getBaseLayer(); } static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict) { SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture); } static jboolean nativeDumpLayerContentToPicture(JNIEnv *env, jobject obj, jint instance, jstring jclassName, jint layerId, jobject pict) { bool success = false; SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); std::string classname = jstringToStdString(env, jclassName); BaseLayerAndroid* baseLayer = reinterpret_cast(instance)->getBaseLayer(); LayerAndroid* layer = baseLayer->findById(layerId); SkSafeRef(layer); if (layer && layer->subclassName() == classname) { LayerContent* content = layer->content(); if (content) { SkCanvas* canvas = picture->beginRecording(content->width(), content->height()); content->draw(canvas); picture->endRecording(); success = true; } } SkSafeUnref(layer); return success; } static bool nativeHasContent(JNIEnv *env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->hasContent(); } static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure) { WebView* view = GET_NATIVE_VIEW(env, obj); ALOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure"); view->setHeightCanMeasure(measure); } static void nativeDestroy(JNIEnv *env, jobject obj, jint ptr) { WebView* view = reinterpret_cast(ptr); ALOGD("nativeDestroy view: %p", view); ALOG_ASSERT(view, "view not set in nativeDestroy"); delete view; } static void nativeStopGL(JNIEnv *env, jobject obj, jint ptr) { if (ptr) reinterpret_cast(ptr)->stopGL(); } static jobject nativeGetSelection(JNIEnv *env, jobject obj) { WebView* view = GET_NATIVE_VIEW(env, obj); ALOG_ASSERT(view, "view not set in %s", __FUNCTION__); String selection = view->getSelection(); return wtfStringToJstring(env, selection); } static void nativeDiscardAllTextures(JNIEnv *env, jobject obj) { //discard all textures for debugging/test purposes, but not gl backing memory bool allTextures = true, deleteGLTextures = false; TilesManager::instance()->discardTextures(allTextures, deleteGLTextures); } static void nativeTileProfilingStart(JNIEnv *env, jobject obj) { TilesManager::instance()->getProfiler()->start(); } static float nativeTileProfilingStop(JNIEnv *env, jobject obj) { return TilesManager::instance()->getProfiler()->stop(); } static void nativeTileProfilingClear(JNIEnv *env, jobject obj) { TilesManager::instance()->getProfiler()->clear(); } static int nativeTileProfilingNumFrames(JNIEnv *env, jobject obj) { return TilesManager::instance()->getProfiler()->numFrames(); } static int nativeTileProfilingNumTilesInFrame(JNIEnv *env, jobject obj, int frame) { return TilesManager::instance()->getProfiler()->numTilesInFrame(frame); } static int nativeTileProfilingGetInt(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey) { WTF::String key = jstringToWtfString(env, jkey); TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile); if (key == "left") return record->left; if (key == "top") return record->top; if (key == "right") return record->right; if (key == "bottom") return record->bottom; if (key == "level") return record->level; if (key == "isReady") return record->isReady ? 1 : 0; return -1; } static float nativeTileProfilingGetFloat(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey) { TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile); return record->scale; } #ifdef ANDROID_DUMP_DISPLAY_TREE static void dumpToFile(const char text[], void* file) { fwrite(text, 1, strlen(text), reinterpret_cast(file)); fwrite("\n", 1, 1, reinterpret_cast(file)); } #endif // Return true to view invalidate WebView static bool nativeSetProperty(JNIEnv *env, jobject obj, jstring jkey, jstring jvalue) { WTF::String key = jstringToWtfString(env, jkey); WTF::String value = jstringToWtfString(env, jvalue); if (key == "inverted") { bool shouldInvert = (value == "true"); TilesManager::instance()->setInvertedScreen(shouldInvert); return true; } else if (key == "inverted_contrast") { float contrast = value.toFloat(); TilesManager::instance()->setInvertedScreenContrast(contrast); return true; } else if (key == "enable_cpu_upload_path") { TilesManager::instance()->transferQueue()->setTextureUploadType( value == "true" ? CpuUpload : GpuUpload); } else if (key == "use_minimal_memory") { TilesManager::instance()->setUseMinimalMemory(value == "true"); } else if (key == "use_double_buffering") { TilesManager::instance()->setUseDoubleBuffering(value == "true"); } else if (key == "tree_updates") { TilesManager::instance()->clearContentUpdates(); } return false; } static jstring nativeGetProperty(JNIEnv *env, jobject obj, jstring jkey) { WTF::String key = jstringToWtfString(env, jkey); if (key == "tree_updates") { int updates = TilesManager::instance()->getContentUpdates(); WTF::String wtfUpdates = WTF::String::number(updates); return wtfStringToJstring(env, wtfUpdates); } return 0; } static void nativeOnTrimMemory(JNIEnv *env, jobject obj, jint level) { if (TilesManager::hardwareAccelerationEnabled()) { // When we got TRIM_MEMORY_MODERATE or TRIM_MEMORY_COMPLETE, we should // make sure the transfer queue is empty and then abandon the Surface // Texture to avoid ANR b/c framework may destroy the EGL context. // Refer to WindowManagerImpl.java for conditions we followed. TilesManager* tilesManager = TilesManager::instance(); if ((level >= TRIM_MEMORY_MODERATE && !tilesManager->highEndGfx()) || level >= TRIM_MEMORY_COMPLETE) { ALOGD("OnTrimMemory with EGL Context %p", eglGetCurrentContext()); tilesManager->cleanupGLResources(); } bool freeAllTextures = (level > TRIM_MEMORY_UI_HIDDEN), glTextures = true; tilesManager->discardTextures(freeAllTextures, glTextures); } } static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) { #ifdef ANDROID_DUMP_DISPLAY_TREE WebView* view = GET_NATIVE_VIEW(env, jwebview); ALOG_ASSERT(view, "view not set in %s", __FUNCTION__); if (view && view->getWebViewCore()) { FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w"); if (file) { SkFormatDumper dumper(dumpToFile, file); // dump the URL if (jurl) { const char* str = env->GetStringUTFChars(jurl, 0); SkDebugf("Dumping %s to %s\n", str, DISPLAY_TREE_LOG_FILE); dumpToFile(str, file); env->ReleaseStringUTFChars(jurl, str); } // now dump the display tree SkDumpCanvas canvas(&dumper); // this will playback the picture into the canvas, which will // spew its contents to the dumper view->draw(&canvas, 0, WebView::DrawExtrasNone); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); } #if USE(ACCELERATED_COMPOSITING) const LayerAndroid* baseLayer = view->getBaseLayer(); if (baseLayer) { FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w"); if (file) { WebCore::FileLayerDumper dumper(file); baseLayer->dumpLayers(&dumper); fclose(file); } } #endif } #endif } static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint nativeView, jint x, jint y, jobject rect, jobject bounds) { WebView* webview = reinterpret_cast(nativeView); ALOG_ASSERT(webview, "webview not set in %s", __FUNCTION__); SkIRect nativeRect, nativeBounds; int id = webview->scrollableLayer(x, y, &nativeRect, &nativeBounds); if (rect) GraphicsJNI::irect_to_jrect(nativeRect, env, rect); if (bounds) GraphicsJNI::irect_to_jrect(nativeBounds, env, bounds); return id; } static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint nativeView, jint layerId, jint x, jint y) { #if ENABLE(ANDROID_OVERFLOW_SCROLL) WebView* webview = reinterpret_cast(nativeView); webview->scrollLayer(layerId, x, y); //TODO: the below only needed for the SW rendering path LayerAndroid* baseLayer = webview->getBaseLayer(); if (!baseLayer) return false; LayerAndroid* layer = baseLayer->findById(layerId); if (!layer || !layer->contentIsScrollable()) return false; return static_cast(layer)->scrollTo(x, y); #endif return false; } static void nativeSetIsScrolling(JNIEnv* env, jobject jwebview, jboolean isScrolling) { // TODO: Pass in the native pointer instead WebView* view = GET_NATIVE_VIEW(env, jwebview); if (view) view->setIsScrolling(isScrolling); } static void nativeUseHardwareAccelSkia(JNIEnv*, jobject, jboolean enabled) { BaseRenderer::setCurrentRendererType(enabled ? BaseRenderer::Ganesh : BaseRenderer::Raster); } static int nativeGetBackgroundColor(JNIEnv* env, jobject obj, jint nativeView) { WebView* view = reinterpret_cast(nativeView); BaseLayerAndroid* baseLayer = view->getBaseLayer(); if (baseLayer) { WebCore::Color color = baseLayer->getBackgroundColor(); if (color.isValid()) return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); } return SK_ColorWHITE; } static void nativeSetPauseDrawing(JNIEnv *env, jobject obj, jint nativeView, jboolean pause) { reinterpret_cast(nativeView)->setDrawingPaused(pause); } static void nativeSetTextSelection(JNIEnv *env, jobject obj, jint nativeView, jint selectionPtr) { SelectText* selection = reinterpret_cast(selectionPtr); reinterpret_cast(nativeView)->setTextSelection(selection); } static jint nativeGetHandleLayerId(JNIEnv *env, jobject obj, jint nativeView, jint handleIndex, jobject cursorPoint, jobject textQuad) { WebView* webview = reinterpret_cast(nativeView); SkIPoint nativePoint; FloatQuad nativeTextQuad; int layerId = webview->getHandleLayerId((SelectText::HandleId) handleIndex, nativePoint, nativeTextQuad); if (cursorPoint) GraphicsJNI::ipoint_to_jpoint(nativePoint, env, cursorPoint); if (textQuad) webview->floatQuadToQuadF(env, nativeTextQuad, textQuad); return layerId; } static void nativeMapLayerRect(JNIEnv *env, jobject obj, jint nativeView, jint layerId, jobject rect) { WebView* webview = reinterpret_cast(nativeView); SkIRect nativeRect; GraphicsJNI::jrect_to_irect(env, rect, &nativeRect); webview->mapLayerRect(layerId, nativeRect); GraphicsJNI::irect_to_jrect(nativeRect, env, rect); } static jint nativeSetHwAccelerated(JNIEnv *env, jobject obj, jint nativeView, jboolean hwAccelerated) { WebView* webview = reinterpret_cast(nativeView); return webview->setHwAccelerated(hwAccelerated); } static void nativeFindMaxVisibleRect(JNIEnv *env, jobject obj, jint nativeView, jint movingLayerId, jobject visibleContentRect) { WebView* webview = reinterpret_cast(nativeView); SkIRect nativeRect; GraphicsJNI::jrect_to_irect(env, visibleContentRect, &nativeRect); webview->findMaxVisibleRect(movingLayerId, nativeRect); GraphicsJNI::irect_to_jrect(nativeRect, env, visibleContentRect); } static bool nativeIsHandleLeft(JNIEnv *env, jobject obj, jint nativeView, jint handleId) { WebView* webview = reinterpret_cast(nativeView); return webview->isHandleLeft(static_cast(handleId)); } static bool nativeIsPointVisible(JNIEnv *env, jobject obj, jint nativeView, jint layerId, jint contentX, jint contentY) { WebView* webview = reinterpret_cast(nativeView); return webview->isPointVisible(layerId, contentX, contentY); } /* * JNI registration */ static JNINativeMethod gJavaWebViewMethods[] = { { "nativeCreate", "(ILjava/lang/String;Z)V", (void*) nativeCreate }, { "nativeDestroy", "(I)V", (void*) nativeDestroy }, { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;II)V", (void*) nativeDraw }, { "nativeCreateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I", (void*) nativeCreateDrawGLFunction }, { "nativeGetDrawGLFunction", "(I)I", (void*) nativeGetDrawGLFunction }, { "nativeUpdateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;F)V", (void*) nativeUpdateDrawGLFunction }, { "nativeDumpDisplayTree", "(Ljava/lang/String;)V", (void*) nativeDumpDisplayTree }, { "nativeEvaluateLayersAnimations", "(I)Z", (void*) nativeEvaluateLayersAnimations }, { "nativeGetSelection", "()Ljava/lang/String;", (void*) nativeGetSelection }, { "nativeSetHeightCanMeasure", "(Z)V", (void*) nativeSetHeightCanMeasure }, { "nativeSetBaseLayer", "(IIZZI)Z", (void*) nativeSetBaseLayer }, { "nativeGetBaseLayer", "(I)I", (void*) nativeGetBaseLayer }, { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", (void*) nativeCopyBaseContentToPicture }, { "nativeDumpLayerContentToPicture", "(ILjava/lang/String;ILandroid/graphics/Picture;)Z", (void*) nativeDumpLayerContentToPicture }, { "nativeHasContent", "()Z", (void*) nativeHasContent }, { "nativeDiscardAllTextures", "()V", (void*) nativeDiscardAllTextures }, { "nativeTileProfilingStart", "()V", (void*) nativeTileProfilingStart }, { "nativeTileProfilingStop", "()F", (void*) nativeTileProfilingStop }, { "nativeTileProfilingClear", "()V", (void*) nativeTileProfilingClear }, { "nativeTileProfilingNumFrames", "()I", (void*) nativeTileProfilingNumFrames }, { "nativeTileProfilingNumTilesInFrame", "(I)I", (void*) nativeTileProfilingNumTilesInFrame }, { "nativeTileProfilingGetInt", "(IILjava/lang/String;)I", (void*) nativeTileProfilingGetInt }, { "nativeTileProfilingGetFloat", "(IILjava/lang/String;)F", (void*) nativeTileProfilingGetFloat }, { "nativeStopGL", "(I)V", (void*) nativeStopGL }, { "nativeScrollableLayer", "(IIILandroid/graphics/Rect;Landroid/graphics/Rect;)I", (void*) nativeScrollableLayer }, { "nativeScrollLayer", "(IIII)Z", (void*) nativeScrollLayer }, { "nativeSetIsScrolling", "(Z)V", (void*) nativeSetIsScrolling }, { "nativeUseHardwareAccelSkia", "(Z)V", (void*) nativeUseHardwareAccelSkia }, { "nativeGetBackgroundColor", "(I)I", (void*) nativeGetBackgroundColor }, { "nativeSetProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*) nativeSetProperty }, { "nativeGetProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*) nativeGetProperty }, { "nativeOnTrimMemory", "(I)V", (void*) nativeOnTrimMemory }, { "nativeSetPauseDrawing", "(IZ)V", (void*) nativeSetPauseDrawing }, { "nativeSetTextSelection", "(II)V", (void*) nativeSetTextSelection }, { "nativeGetHandleLayerId", "(IILandroid/graphics/Point;Landroid/webkit/QuadF;)I", (void*) nativeGetHandleLayerId }, { "nativeMapLayerRect", "(IILandroid/graphics/Rect;)V", (void*) nativeMapLayerRect }, { "nativeSetHwAccelerated", "(IZ)I", (void*) nativeSetHwAccelerated }, { "nativeFindMaxVisibleRect", "(IILandroid/graphics/Rect;)V", (void*) nativeFindMaxVisibleRect }, { "nativeIsHandleLeft", "(II)Z", (void*) nativeIsHandleLeft }, { "nativeIsPointVisible", "(IIII)Z", (void*) nativeIsPointVisible }, }; int registerWebView(JNIEnv* env) { jclass clazz = env->FindClass("android/webkit/WebViewClassic"); ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebViewClassic"); gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I"); ALOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebViewClassic.mNativeClass"); env->DeleteLocalRef(clazz); return jniRegisterNativeMethods(env, "android/webkit/WebViewClassic", gJavaWebViewMethods, NELEM(gJavaWebViewMethods)); } } // namespace android