diff options
Diffstat (limited to 'WebKit/android')
58 files changed, 3524 insertions, 1038 deletions
diff --git a/WebKit/android/RenderSkinAndroid.cpp b/WebKit/android/RenderSkinAndroid.cpp index d148262..00f2b96 100644 --- a/WebKit/android/RenderSkinAndroid.cpp +++ b/WebKit/android/RenderSkinAndroid.cpp @@ -29,6 +29,7 @@ #include "RenderSkinAndroid.h" #include "RenderSkinButton.h" #include "RenderSkinCombo.h" +#include "RenderSkinMediaButton.h" #include "RenderSkinRadio.h" #include "SkImageDecoder.h" @@ -45,7 +46,8 @@ RenderSkinAndroid::RenderSkinAndroid() void RenderSkinAndroid::Init(android::AssetManager* am, String drawableDirectory) { RenderSkinButton::Init(am, drawableDirectory); - RenderSkinCombo::Init(am); + RenderSkinCombo::Init(am, drawableDirectory); + RenderSkinMediaButton::Init(am, drawableDirectory); RenderSkinRadio::Init(am, drawableDirectory); } diff --git a/WebKit/android/RenderSkinButton.cpp b/WebKit/android/RenderSkinButton.cpp index 63f545a..ff739f6 100644 --- a/WebKit/android/RenderSkinButton.cpp +++ b/WebKit/android/RenderSkinButton.cpp @@ -26,7 +26,6 @@ #define LOG_TAG "WebCore" #include "config.h" -#include "CString.h" #include "android_graphics.h" #include "Document.h" #include "IntRect.h" @@ -37,6 +36,7 @@ #include "SkRect.h" #include <utils/Debug.h> #include <utils/Log.h> +#include <wtf/text/CString.h> struct PatchData { const char* name; diff --git a/WebKit/android/RenderSkinCombo.cpp b/WebKit/android/RenderSkinCombo.cpp index 6f88ee3..b30dc29 100644 --- a/WebKit/android/RenderSkinCombo.cpp +++ b/WebKit/android/RenderSkinCombo.cpp @@ -33,67 +33,113 @@ #include "RenderStyle.h" #include "SkCanvas.h" #include "SkNinePatch.h" +#include <wtf/text/CString.h> namespace WebCore { -static SkBitmap s_bitmap[2]; // Collection of assets for a combo box -static bool s_decoded; // True if all assets were decoded -static const int s_margin = 2; -static const SkIRect s_mar = { s_margin, s_margin, - RenderSkinCombo::extraWidth(), s_margin }; -static SkIRect s_subset; +// Indicates if the entire asset is being drawn, or if the border is being +// excluded and just the arrow drawn. +enum BorderStyle { + FullAsset, + NoBorder +}; +// There are 2.5 different concepts of a 'border' here, which results +// in rather a lot of magic constants. In each case, there are 2 +// numbers, one for medium res and one for high-res. All sizes are in pixels. -RenderSkinCombo::RenderSkinCombo() -{ -} +// Firstly, we have the extra padding that webkit needs to know about, +// which defines how much bigger this element is made by the +// asset. This is actually a bit broader than the actual border on the +// asset, to make things look less cramped. The border is the same +// width on all sides, except on the right when it's significantly +// wider to allow for the arrow. +const int RenderSkinCombo::arrowMargin[2] = {22, 34}; +const int RenderSkinCombo::padMargin[2] = {2, 5}; + +// Then we have the borders used for the 9-patch stretch. The +// rectangle at the centre of these borders is entirely below and to +// the left of the arrow in the asset. Hence the border widths are the +// same for the bottom and left, but are different for the top. The +// right hand border width happens to be the same as arrowMargin +// defined above. +static const int stretchMargin[2] = {3, 5}; // border width for the bottom and left of the 9-patch +static const int stretchTop[2] = {15, 23}; // border width for the top of the 9-patch + +// Finally, if the border is defined by the CSS, we only draw the +// arrow and not the border. We do this by drawing the relevant subset +// of the bitmap, which must now be precisely determined by what's in +// the asset with no extra padding to make things look properly +// spaced. The border to remove at the top, right and bottom of the +// image is the same as stretchMargin above, but we need to know the width +// of the arrow. +static const int arrowWidth[2] = {22, 31}; + +RenderSkinCombo::Resolution RenderSkinCombo::resolution = MedRes; + +const SkIRect RenderSkinCombo::margin[2][2] = {{{ stretchMargin[MedRes], stretchTop[MedRes], + RenderSkinCombo::arrowMargin[MedRes] + stretchMargin[MedRes], stretchMargin[MedRes] }, + {0, stretchTop[MedRes], 0, stretchMargin[MedRes]}}, + {{ stretchMargin[HighRes], stretchTop[HighRes], + RenderSkinCombo::arrowMargin[HighRes] + stretchMargin[HighRes], stretchMargin[HighRes] }, + {0, stretchTop[HighRes], 0, stretchMargin[HighRes]}}}; +static SkBitmap bitmaps[2][2]; // Collection of assets for a combo box +static bool isDecoded; // True if all assets were decoded -void RenderSkinCombo::Init(android::AssetManager* am) +void RenderSkinCombo::Init(android::AssetManager* am, String drawableDirectory) { - if (s_decoded) + if (isDecoded) return; - // Maybe short circuiting is fine, since I don't even draw if one state is not decoded properly - // but is that necessary in the final version? - s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-noHighlight.png", &s_bitmap[kNormal]); - s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-disabled.png", &s_bitmap[kDisabled]) && s_decoded; - - int width = s_bitmap[kNormal].width(); - int height = s_bitmap[kNormal].height(); - s_subset.set(width - RenderSkinCombo::extraWidth() + s_margin, 0, width, height); + + if (drawableDirectory[drawableDirectory.length() - 5] == 'h') + resolution = HighRes; + + isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]); + isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]); + + int width = bitmaps[kNormal][FullAsset].width(); + int height = bitmaps[kNormal][FullAsset].height(); + SkIRect subset; + subset.set(width - arrowWidth[resolution], 0, width, height); + bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset); + bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset); } bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height) { - if (!s_decoded) + if (!isDecoded) return true; State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled; - if (height < (s_margin<<1) + 1) { - height = (s_margin<<1) + 1; - } + height = std::max(height, (stretchMargin[resolution]<<1) + 1); + SkRect bounds; + BorderStyle drawBorder = FullAsset; bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1)); RenderStyle* style = element->renderStyle(); SkPaint paint; - paint.setColor(style->backgroundColor().rgb()); + paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb()); canvas->drawRect(bounds, paint); bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height)); - if (style->borderLeftColor().isValid() || - style->borderRightColor().isValid() || - style->borderTopColor().isValid() || - style->borderBottomColor().isValid()) { + // If this is an appearance where RenderTheme::paint returns true + // without doing anything, this means that + // RenderBox::PaintBoxDecorationWithSize will end up painting the + // border, so we shouldn't paint a border here. + if (style->appearance() == MenulistButtonPart || + style->appearance() == ListboxPart || + style->appearance() == TextFieldPart || + style->appearance() == TextAreaPart) { bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth()); bounds.fRight -= SkIntToScalar(style->borderRightWidth()); bounds.fTop += SkIntToScalar(style->borderTopWidth()); bounds.fBottom -= SkIntToScalar(style->borderBottomWidth()); - canvas->drawBitmapRect(s_bitmap[state], &s_subset, bounds); - } else { - SkNinePatch::DrawNine(canvas, bounds, s_bitmap[state], s_mar); + drawBorder = NoBorder; } + SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[resolution][drawBorder]); return false; } diff --git a/WebKit/android/RenderSkinCombo.h b/WebKit/android/RenderSkinCombo.h index 91c9367..38cd048 100644 --- a/WebKit/android/RenderSkinCombo.h +++ b/WebKit/android/RenderSkinCombo.h @@ -37,13 +37,10 @@ namespace WebCore { class RenderSkinCombo : public RenderSkinAndroid { public: - RenderSkinCombo(); - virtual ~RenderSkinCombo() {} - /** * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use. */ - static void Init(android::AssetManager*); + static void Init(android::AssetManager*, String drawableDirectory); /** * Draw the provided Node on the SkCanvas, using the dimensions provided by @@ -53,11 +50,18 @@ public: static bool Draw(SkCanvas* , Node* , int x, int y, int w, int h); // The image is wider than the RenderObject, so this accounts for that. - static int extraWidth() { return arrowMargin; } - + static int extraWidth() { return arrowMargin[resolution]; } + static int padding() { return padMargin[resolution]; } + + enum Resolution { + MedRes, + HighRes + }; private: - - static const int arrowMargin = 22; + static Resolution resolution; + const static int arrowMargin[2]; + const static int padMargin[2]; + const static SkIRect margin[2][2]; }; } // WebCore diff --git a/WebKit/android/RenderSkinMediaButton.cpp b/WebKit/android/RenderSkinMediaButton.cpp new file mode 100644 index 0000000..9055e89 --- /dev/null +++ b/WebKit/android/RenderSkinMediaButton.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2010, 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 "WebCore" + +#include "config.h" +#include "android_graphics.h" +#include "Document.h" +#include "IntRect.h" +#include "Node.h" +#include "RenderSkinMediaButton.h" +#include "SkCanvas.h" +#include "SkNinePatch.h" +#include "SkRect.h" +#include <utils/Debug.h> +#include <utils/Log.h> +#include <wtf/text/CString.h> + +struct PatchData { + const char* name; + int8_t outset, margin; +}; + +static const PatchData gFiles[] = + { + { "btn_media_player.9.png", 0, 0 }, // DEFAULT BGD BUTTON + { "ic_media_pause.png", 0, 0}, // PAUSE + { "ic_media_play.png", 0, 0 }, // PLAY + { "ic_media_pause.png", 0, 0 }, // MUTE + { "ic_media_rew.png", 0, 0 }, // REWIND + { "ic_media_ff.png", 0, 0 }, // FORWARD + { "btn_media_player_disabled.9.png", 0, 0 }, // BACKGROUND_SLIDER + { "btn_media_player_pressed.9.png", 0, 0 }, // SLIDER_TRACK + { "btn_media_player.9.png", 0, 0 } // SLIDER_THUMB + }; + +static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])]; +static bool gDecoded; +static bool gHighRes; + +namespace WebCore { + +void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirectory) +{ + static bool gInited; + if (gInited) + return; + + gInited = true; + gDecoded = true; + gHighRes = drawableDirectory[drawableDirectory.length() - 5] == 'h'; + for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) { + String path = drawableDirectory + gFiles[i].name; + if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) { + gDecoded = false; + LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw"); + break; + } + } +} + +void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType) +{ + // If we failed to decode, do nothing. This way the browser still works, + // and webkit will still draw the label and layout space for us. + if (!gDecoded) { + return; + } + + bool drawsNinePatch = true; + bool drawsImage = true; + bool drawsBackgroundColor = false; + + int ninePatchIndex = 0; + int imageIndex = 0; + + SkRect bounds(r); + SkScalar imageMargin = 8; + SkPaint paint; + SkColor backgroundColor = SkColorSetARGB(255, 200, 200, 200); + SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100); + + switch (buttonType) { + case PAUSE: + case PLAY: + case MUTE: + case REWIND: + case FORWARD: + { + imageIndex = buttonType + 1; + drawsBackgroundColor = true; + paint.setColor(backgroundColor); + break; + } + case BACKGROUND_SLIDER: + { + drawsImage = false; + drawsNinePatch = false; + drawsBackgroundColor = true; + paint.setColor(backgroundColor); + break; + } + case SLIDER_TRACK: + { + drawsImage = false; + drawsNinePatch = false; + drawsBackgroundColor = true; + paint.setColor(trackBackgroundColor); + bounds.fTop += 8; + bounds.fBottom -= 8; + break; + } + case SLIDER_THUMB: + { + drawsImage = false; + ninePatchIndex = buttonType + 1; + break; + } + default: + drawsImage = false; + drawsNinePatch = false; + } + + if (drawsBackgroundColor) { + canvas->drawRect(r, paint); + } + + if (drawsNinePatch) { + const PatchData& pd = gFiles[ninePatchIndex]; + int marginValue = pd.margin + pd.outset; + + SkIRect margin; + margin.set(marginValue, marginValue, marginValue, marginValue); + SkNinePatch::DrawNine(canvas, bounds, gButton[0], margin); + } + + if (drawsImage) { + SkScalar SIZE = gButton[imageIndex].width(); + SkScalar width = r.width(); + SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE); + int saveScaleCount = canvas->save(); + canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin); + canvas->scale(scale, scale); + canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint); + canvas->restoreToCount(saveScaleCount); + } +} + +} //WebCore diff --git a/WebKit/android/RenderSkinMediaButton.h b/WebKit/android/RenderSkinMediaButton.h new file mode 100644 index 0000000..b4e99f4 --- /dev/null +++ b/WebKit/android/RenderSkinMediaButton.h @@ -0,0 +1,61 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef RenderSkinMediaButton_h +#define RenderSkinMediaButton_h + +#include "RenderSkinAndroid.h" + +class SkCanvas; + +namespace WebCore { +class IntRect; +class RenderSkinMediaButton +{ +public: + /** + * Initialize the class before use. Uses the AssetManager to initialize any + * bitmaps the class may use. + */ + static void Init(android::AssetManager*, String drawableDirectory); + /** + * Draw the skin to the canvas, using the rectangle for its bounds and the + * State to determine which skin to use, i.e. focused or not focused. + */ + static void Draw(SkCanvas* , const IntRect& , int buttonType); + /** + * Button types + */ + enum { PAUSE, PLAY, MUTE, REWIND, FORWARD, BACKGROUND_SLIDER, SLIDER_TRACK, SLIDER_THUMB }; + /** + * Slider dimensions + */ + static int sliderThumbWidth() { return 10; } + static int sliderThumbHeight() { return 30; } + +}; + +} // WebCore +#endif // RenderSkinMediaButton_h diff --git a/WebKit/android/RenderSkinRadio.cpp b/WebKit/android/RenderSkinRadio.cpp index a1fc659..ef26f01 100644 --- a/WebKit/android/RenderSkinRadio.cpp +++ b/WebKit/android/RenderSkinRadio.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "RenderSkinRadio.h" -#include "CString.h" #include "android_graphics.h" #include "Document.h" #include "Element.h" @@ -37,6 +36,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkRect.h" +#include <wtf/text/CString.h> static const char* checks[] = { "btn_check_off.png", "btn_check_on.png", diff --git a/WebKit/android/TimeCounter.cpp b/WebKit/android/TimeCounter.cpp index da09b07..4062bc2 100644 --- a/WebKit/android/TimeCounter.cpp +++ b/WebKit/android/TimeCounter.cpp @@ -28,17 +28,16 @@ #include "config.h" #include "TimeCounter.h" -#include "CString.h" #include "Cache.h" #include "KURL.h" #include "Node.h" #include "SystemTime.h" #include "StyleBase.h" -#include <utils/Log.h> -#include <wtf/CurrentTime.h> - #include <sys/time.h> #include <time.h> +#include <utils/Log.h> +#include <wtf/CurrentTime.h> +#include <wtf/text/CString.h> #if USE(JSC) #include "JSDOMWindow.h" @@ -94,7 +93,7 @@ static const char* timeCounterNames[] = { "javascript execution", "calculate style", "Java callback (frame bridge)", - "parsing (may include calcStyle or Java callback)", + "parsing (may include calcStyle, Java callback or inline script execution)", "layout", "native 1 (frame bridge)", "native 2 (resource load)", diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp index c258d79..30ac36c 100644 --- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp @@ -29,7 +29,6 @@ #include "ApplicationCacheStorage.h" #include "ChromeClientAndroid.h" -#include "CString.h" #include "DatabaseTracker.h" #include "Document.h" #include "PlatformString.h" @@ -39,14 +38,15 @@ #include "FrameView.h" #include "Geolocation.h" #include "GraphicsLayerAndroid.h" +#include "Icon.h" #include "Page.h" -#include "Screen.h" #include "ScriptController.h" #include "WebCoreFrameBridge.h" #include "WebCoreViewBridge.h" #include "WebViewCore.h" #include "WindowFeatures.h" #include "Settings.h" +#include <wtf/text/CString.h> namespace android { @@ -56,28 +56,19 @@ static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedin #if USE(ACCELERATED_COMPOSITING) -void ChromeClientAndroid::syncTimerFired(Timer<ChromeClientAndroid>* client) +WebCore::GraphicsLayer* ChromeClientAndroid::layersSync() { - if (!m_rootGraphicsLayer) - return; - - if (m_webFrame) { - FrameView* frameView = m_webFrame->page()->mainFrame()->view(); - if (frameView && frameView->syncCompositingStateRecursive()) { - GraphicsLayerAndroid* androidGraphicsLayer = - static_cast<GraphicsLayerAndroid*>(m_rootGraphicsLayer); - if (androidGraphicsLayer) { - androidGraphicsLayer->sendImmediateRepaint(); - androidGraphicsLayer->notifyClientAnimationStarted(); - } - } + if (m_rootGraphicsLayer && m_needsLayerSync && m_webFrame) { + if (FrameView* frameView = m_webFrame->page()->mainFrame()->view()) + frameView->syncCompositingStateRecursive(); } + m_needsLayerSync = false; + return m_rootGraphicsLayer; } void ChromeClientAndroid::scheduleCompositingLayerSync() { - if (!m_syncTimer.isActive()) - m_syncTimer.startOneShot(0); + m_needsLayerSync = true; } void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization() @@ -85,15 +76,12 @@ void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization() // This should not be needed } -void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame* frame, WebCore::GraphicsLayer* layer) +void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* layer) { + // frame is not used in Android as we should only get root graphics layer for the main frame m_rootGraphicsLayer = layer; - if (!layer) { - WebViewCore::getWebViewCore(frame->view())->setUIRootLayer(0); + if (!layer) return; - } - WebCore::GraphicsLayerAndroid* androidGraphicsLayer = static_cast<GraphicsLayerAndroid*>(layer); - androidGraphicsLayer->setFrame(frame); scheduleCompositingLayerSync(); } @@ -166,10 +154,10 @@ Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&, return frame->page(); #endif - WTF::PassRefPtr<WebCore::Screen> screen = WebCore::Screen::create(frame); + const WebCoreViewBridge* bridge = frame->view()->platformWidget(); bool dialog = features.dialog || !features.resizable - || (features.heightSet && features.height < screen.get()->height() - && features.widthSet && features.width < screen.get()->width()) + || (features.heightSet && features.height < bridge->height() + && features.widthSet && features.width < bridge->width()) || (!features.menuBarVisible && !features.statusBarVisible && !features.toolBarVisible && !features.locationBarVisible && !features.scrollbarsVisible); @@ -276,12 +264,19 @@ bool ChromeClientAndroid::tabsToLinks() const { return false; } IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); } -// new to change 38068 (Nov 6, 2008) -void ChromeClientAndroid::repaint(const IntRect& rect, bool contentChanged, - bool immediate, bool repaintContentOnly) { - notImplemented(); -// was in ScrollViewAndroid::update() : needs to be something like: -// android::WebViewCore::getWebViewCore(this)->contentInvalidate(rect); +void ChromeClientAndroid::invalidateWindow(const IntRect&, bool) +{ + notImplemented(); +} + +void ChromeClientAndroid::invalidateContentsAndWindow(const IntRect& updateRect, bool /*immediate*/) +{ + notImplemented(); +} + +void ChromeClientAndroid::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate) +{ + notImplemented(); } // new to change 38068 (Nov 6, 2008) @@ -461,7 +456,7 @@ void ChromeClientAndroid::requestGeolocationPermissionForFrame(Frame* frame, Geo m_geolocationPermissions->queryPermissionState(frame); } -void ChromeClientAndroid::cancelGeolocationPermissionRequestForFrame(Frame* frame) +void ChromeClientAndroid::cancelGeolocationPermissionRequestForFrame(Frame* frame, WebCore::Geolocation*) { if (m_geolocationPermissions) m_geolocationPermissions->cancelPermissionStateQuery(frame); @@ -492,6 +487,11 @@ void ChromeClientAndroid::runOpenPanel(Frame* frame, core->openFileChooser(chooser); } +void ChromeClientAndroid::chooseIconForFiles(const Vector<WebCore::String>&, FileChooser*) +{ + notImplemented(); +} + bool ChromeClientAndroid::setCursor(PlatformCursorHandle) { notImplemented(); @@ -514,4 +514,14 @@ void ChromeClientAndroid::needTouchEvents(bool needTouchEvents) } #endif +#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS) +void ChromeClientAndroid::webAppCanBeInstalled() +{ + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView); + if (core) + core->notifyWebAppCanBeInstalled(); +} +#endif + } diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.h b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h index 68cac24..a1f097c 100644 --- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.h +++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h @@ -46,8 +46,7 @@ namespace android { ChromeClientAndroid() : m_webFrame(0), m_geolocationPermissions(0) #if USE(ACCELERATED_COMPOSITING) , m_rootGraphicsLayer(0) - , m_askToDrawAgain(false) - , m_syncTimer(this, &ChromeClientAndroid::syncTimerFired) + , m_needsLayerSync(false) #endif , m_triedToReclaimDBQuota(false) { } @@ -113,7 +112,9 @@ namespace android { virtual IntRect windowResizerRect() const; // Methods used by HostWindow. - virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false); + virtual void invalidateWindow(const WebCore::IntRect&, bool); + virtual void invalidateContentsAndWindow(const WebCore::IntRect&, bool); + virtual void invalidateContentsForSlowScroll(const WebCore::IntRect&, bool); virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect); virtual IntPoint screenToWindow(const IntPoint&) const; virtual IntRect windowToScreen(const IntRect&) const; @@ -143,7 +144,7 @@ namespace android { // Methods used to request and provide Geolocation permissions. virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*); - virtual void cancelGeolocationPermissionRequestForFrame(Frame*); + virtual void cancelGeolocationPermissionRequestForFrame(WebCore::Frame*, WebCore::Geolocation*); // Android-specific void provideGeolocationPermissions(const String &origin, bool allow, bool remember); void storeGeolocationPermissions(); @@ -151,6 +152,7 @@ namespace android { virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>); virtual bool setCursor(PlatformCursorHandle); + virtual void chooseIconForFiles(const WTF::Vector<WebCore::String>&, FileChooser*); // Notification that the given form element has changed. This function // will be called frequently, so handling should be very fast. @@ -160,13 +162,18 @@ namespace android { // Android-specific void setWebFrame(android::WebFrame* webframe); + android::WebFrame* webFrame() { return m_webFrame; } void wakeUpMainThreadWithNewQuota(long newQuota); #if USE(ACCELERATED_COMPOSITING) virtual void attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* g); virtual void setNeedsOneShotDrawingSynchronization(); virtual void scheduleCompositingLayerSync(); - void syncTimerFired(Timer<ChromeClientAndroid>*); + WebCore::GraphicsLayer* layersSync(); +#endif + +#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS) + virtual void webAppCanBeInstalled(); #endif private: @@ -175,8 +182,7 @@ namespace android { OwnPtr<GeolocationPermissions> m_geolocationPermissions; #if USE(ACCELERATED_COMPOSITING) WebCore::GraphicsLayer* m_rootGraphicsLayer; - bool m_askToDrawAgain; - Timer<ChromeClientAndroid> m_syncTimer; + bool m_needsLayerSync; #endif WTF::ThreadCondition m_quotaThreadCondition; WTF::Mutex m_quotaThreadLock; diff --git a/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp index c07ea71..718a30c 100644 --- a/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp @@ -32,12 +32,12 @@ #include "EventNames.h" #include "FocusController.h" #include "Frame.h" -#include "KeyboardCodes.h" #include "KeyboardEvent.h" #include "NotImplemented.h" #include "PlatformKeyboardEvent.h" #include "PlatformString.h" #include "WebViewCore.h" +#include "WindowsKeyboardCodes.h" namespace android { diff --git a/WebKit/android/WebCoreSupport/FileSystemClient.h b/WebKit/android/WebCoreSupport/FileSystemClient.h new file mode 100644 index 0000000..5bde18a --- /dev/null +++ b/WebKit/android/WebCoreSupport/FileSystemClient.h @@ -0,0 +1,41 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef FILESYSTEM_CLIENT_H +#define FILESYSTEM_CLIENT_H + +#include "PlatformString.h" + +using namespace WebCore; + +namespace android { + +class FileSystemClient { +public: + virtual ~FileSystemClient() { } + virtual String resolveFilePathForContentUri(const String&) = 0; +}; +} +#endif diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp index 9112afe..9278bf8 100644 --- a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp @@ -29,9 +29,10 @@ #include "FrameLoaderClientAndroid.h" #include "BackForwardList.h" -#include "CString.h" #include "CachedFrame.h" #include "CachedFramePlatformDataAndroid.h" +#include "Chrome.h" +#include "ChromeClientAndroid.h" #include "DOMImplementation.h" #include "Document.h" #include "DocumentLoader.h" @@ -75,6 +76,7 @@ #include "android_graphics.h" #include <utils/AssetManager.h> +#include <wtf/text/CString.h> extern android::AssetManager* globalAssetManager(); @@ -393,7 +395,9 @@ void FrameLoaderClientAndroid::dispatchDidFinishLoad() { void FrameLoaderClientAndroid::dispatchDidFirstLayout() { ASSERT(m_frame); - m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); + // set EXTRA_LAYOUT_DELAY if the loader is not completed yet + if (!m_frame->loader()->isComplete()) + m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout // so that about:blank will update the screen. if (!m_frame->tree()->parent()) { @@ -459,8 +463,11 @@ void FrameLoaderClientAndroid::dispatchDecidePolicyForMIMEType(FramePolicyFuncti ASSERT(func); if (!func) return; + + PolicyChecker* policy = m_frame->loader()->policyChecker(); + if (request.isNull()) { - (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); + (policy->*func)(PolicyIgnore); return; } // Default to Use (display internally). @@ -468,30 +475,33 @@ void FrameLoaderClientAndroid::dispatchDecidePolicyForMIMEType(FramePolicyFuncti // Check if we should Download instead. const ResourceResponse& response = m_frame->loader()->activeDocumentLoader()->response(); const String& content_disposition = response.httpHeaderField("Content-Disposition"); - if (!content_disposition.isEmpty()) { + if (!content_disposition.isEmpty() && + TreatAsAttachment(content_disposition)) { // Server wants to override our normal policy. - if (TreatAsAttachment(content_disposition)) { - // Check to see if we are a sub frame (main frame has no owner element) - if (m_frame->ownerElement() != 0) - action = PolicyIgnore; - else - action = PolicyDownload; - } - } else { - // Ask if it can be handled internally. - if (!canShowMIMEType(MIMEType)) { - // Check to see if we are a sub frame (main frame has no owner element) - if (m_frame->ownerElement() != 0) - action = PolicyIgnore; - else - action = PolicyDownload; - } + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + (policy->*func)(action); + return; + } + + // Ask if it can be handled internally. + if (!canShowMIMEType(MIMEType)) { + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + (policy->*func)(action); + return; } // A status code of 204 indicates no content change. Ignore the result. WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); if (docLoader->response().httpStatusCode() == 204) action = PolicyIgnore; - (m_frame->loader()->policyChecker()->*func)(action); + (policy->*func)(action); } void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func, @@ -693,7 +703,7 @@ void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* bool userChosen = !encoding.isNull(); if (encoding.isNull()) encoding = loader->response().textEncodingName(); - loader->frameLoader()->setEncoding(encoding, userChosen); + loader->frameLoader()->writer()->setEncoding(encoding, userChosen); Document *doc = m_frame->document(); if (doc) loader->frameLoader()->addData(data, length); @@ -800,7 +810,7 @@ void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) { if (!m_frame->tree()->parent()) { WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); bridge->setScale((int)(webViewCore->scale() * 100)); - bridge->setScreenWidthScale((int)(webViewCore->screenWidthScale() * 100)); + bridge->setTextWrapScale((int)(webViewCore->textWrapScale() * 100)); } WebCore::notifyHistoryItemChanged(item); @@ -812,11 +822,7 @@ void FrameLoaderClientAndroid::restoreViewState() { AndroidWebHistoryBridge* bridge = item->bridge(); // restore the scale (only) for the top frame if (!m_frame->tree()->parent()) { - int scale = bridge->scale(); - webViewCore->restoreScale(scale); - int screenWidthScale = bridge->screenWidthScale(); - if (screenWidthScale != scale) - webViewCore->restoreScreenWidthScale(screenWidthScale); + webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale()); } } @@ -899,14 +905,17 @@ void FrameLoaderClientAndroid::transitionToCommittedForNewPage() { // Save the old WebFrameView's bounds and apply them to the new WebFrameView WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (m_frame->view()->platformWidget()); IntRect bounds = oldWebFrameView->getBounds(); + IntRect visBounds = oldWebFrameView->getVisibleBounds(); IntRect windowBounds = oldWebFrameView->getWindowBounds(); WebCore::FrameView* oldFrameView = oldWebFrameView->view(); - m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(), IntSize(), false); + m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(), + oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout()); // Create a new WebFrameView for the new FrameView WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore); newFrameView->setLocation(bounds.x(), bounds.y()); newFrameView->setSize(bounds.width(), bounds.height()); + newFrameView->setVisibleSize(visBounds.width(), visBounds.height()); newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height()); // newFrameView attaches itself to FrameView which Retains the reference, so // call Release for newFrameView @@ -1215,6 +1224,21 @@ WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const I return 0; } +void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument() +{ + ASSERT(m_frame); + // m_webFrame points to the WebFrame for the page that our frame previously + // belonged to. If the frame now belongs to a new page, we need to update + // m_webFrame to point to the WebFrame for the new page. + Page* newPage = m_frame->page(); + if (newPage != m_webFrame->page()) { + ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client()); + Release(m_webFrame); + m_webFrame = chromeClient->webFrame(); + Retain(m_webFrame); + } +} + // This function is used by the <OBJECT> element to determine the type of // the contents and work out if it can render it. ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, @@ -1281,4 +1305,8 @@ void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) { } } +void FrameLoaderClientAndroid::dispatchDidChangeIcons() { + notImplemented(); +} + } diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h index 3b754b8..ddbe196 100644 --- a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h +++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h @@ -176,11 +176,9 @@ namespace android { virtual bool canCachePage() const; virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&); - virtual WTF::PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, - const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight); - virtual WTF::PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, - const WTF::Vector<String>&, const WTF::Vector<String>&, - const String&, bool loadManually); + virtual WTF::PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight); + virtual void didTransferChildFrameToNewDocument(); + virtual WTF::PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const WTF::Vector<String>&, const WTF::Vector<String>&, const String&, bool loadManually); virtual void redirectDataToPlugin(Widget* pluginWidget); virtual WTF::PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL& baseURL, const WTF::Vector<String>& paramNames, const WTF::Vector<String>& paramValues); @@ -210,6 +208,9 @@ namespace android { CacheBuilder& getCacheBuilder() { return m_cacheBuilder; } void enableOnDemandPlugins() { m_onDemandPluginsEnabled = true; } + + void dispatchDidChangeIcons(); + void dispatchWillSendSubmitEvent(HTMLFormElement*) { } private: CacheBuilder m_cacheBuilder; Frame* m_frame; diff --git a/WebKit/android/WebCoreSupport/InspectorClientAndroid.h b/WebKit/android/WebCoreSupport/InspectorClientAndroid.h index 6feb2d7..e3d2e69 100644 --- a/WebKit/android/WebCoreSupport/InspectorClientAndroid.h +++ b/WebKit/android/WebCoreSupport/InspectorClientAndroid.h @@ -36,27 +36,15 @@ public: virtual void inspectorDestroyed() { delete this; } - virtual Page* createPage() { return NULL; } - - virtual String localizedStringsURL() { return String(); } - - virtual void showWindow() {} - virtual void closeWindow() {} - - virtual void attachWindow() {} - virtual void detachWindow() {} - - virtual void setAttachedWindowHeight(unsigned height) {} + virtual void openInspectorFrontend(WebCore::InspectorController*) {} virtual void highlight(Node*) {} virtual void hideHighlight() {} - virtual void inspectedURLChanged(const String& newURL) {} - virtual void populateSetting(const String& key, String* value) {} virtual void storeSetting(const String& key, const String& value) {} - virtual String hiddenPanels() { return String(); } - virtual void inspectorWindowObjectCleared() {} + + virtual bool sendMessageToFrontend(const WebCore::String&) { return false; } }; } diff --git a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp index bfb5305..6224707 100644 --- a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp +++ b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp @@ -30,7 +30,6 @@ #include "GraphicsContext.h" #include "SkiaUtils.h" -#include "TimeRanges.h" #include "WebCoreJni.h" #include "WebViewCore.h" @@ -44,16 +43,22 @@ using namespace android; namespace WebCore { static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy"; +static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio"; struct MediaPlayerPrivate::JavaGlue { jobject m_javaProxy; - jmethodID m_getInstance; jmethodID m_play; jmethodID m_teardown; - jmethodID m_loadPoster; jmethodID m_seek; jmethodID m_pause; + // Audio + jmethodID m_newInstance; + jmethodID m_setDataSource; + jmethodID m_getMaxTimeSeekable; + // Video + jmethodID m_getInstance; + jmethodID m_loadPoster; }; MediaPlayerPrivate::~MediaPlayerPrivate() @@ -65,7 +70,6 @@ MediaPlayerPrivate::~MediaPlayerPrivate() env->DeleteGlobalRef(m_glue->m_javaProxy); } } - delete m_glue; } @@ -74,29 +78,6 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) registrar(create, getSupportedTypes, supportsType); } -void MediaPlayerPrivate::load(const String& url) -{ - // Just save the URl. - m_url = url; -} - -void MediaPlayerPrivate::cancelLoad() -{ -} - -void MediaPlayerPrivate::play() -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - if (!env || !m_glue->m_javaProxy || !m_url.length()) - return; - - m_paused = false; - jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length()); - env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl); - env->DeleteLocalRef(jUrl); - checkException(env); -} - void MediaPlayerPrivate::pause() { JNIEnv* env = JSC::Bindings::getJNIEnv(); @@ -108,22 +89,6 @@ void MediaPlayerPrivate::pause() checkException(env); } -IntSize MediaPlayerPrivate::naturalSize() const -{ - return m_naturalSize; -} - -bool MediaPlayerPrivate::hasAudio() const -{ - // TODO - return false; -} - -bool MediaPlayerPrivate::hasVideo() const -{ - return m_hasVideo; -} - void MediaPlayerPrivate::setVisible(bool visible) { m_isVisible = visible; @@ -131,99 +96,19 @@ void MediaPlayerPrivate::setVisible(bool visible) createJavaPlayerIfNeeded(); } -float MediaPlayerPrivate::duration() const -{ - return m_duration; -} - -float MediaPlayerPrivate::currentTime() const -{ - return m_currentTime; -} - void MediaPlayerPrivate::seek(float time) { JNIEnv* env = JSC::Bindings::getJNIEnv(); - if (!env || !m_glue->m_javaProxy || !m_url.length()) + if (!env || !m_url.length()) return; - m_currentTime = time; - env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f)); + if (m_glue->m_javaProxy) { + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f)); + m_currentTime = time; + } checkException(env); } -bool MediaPlayerPrivate::seeking() const -{ - return false; -} - -void MediaPlayerPrivate::setEndTime(float) -{ -} - -void MediaPlayerPrivate::setRate(float) -{ -} - -bool MediaPlayerPrivate::paused() const -{ - return m_paused; -} - -void MediaPlayerPrivate::setVolume(float) -{ -} - -MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const -{ - return m_networkState; -} - -MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const -{ - return m_readyState; -} - -float MediaPlayerPrivate::maxTimeSeekable() const -{ - return 0; -} - -PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const -{ - return TimeRanges::create(); -} - -int MediaPlayerPrivate::dataRate() const -{ - return 0; -} - -unsigned MediaPlayerPrivate::totalBytes() const -{ - return 0; -} - -unsigned MediaPlayerPrivate::bytesLoaded() const -{ - return 0; -} - -void MediaPlayerPrivate::setSize(const IntSize&) -{ -} - -void MediaPlayerPrivate::setPoster(const String& url) -{ - m_posterUrl = url; - JNIEnv* env = JSC::Bindings::getJNIEnv(); - if (!env || !m_glue->m_javaProxy || !m_posterUrl.length()) - return; - // Send the poster - jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length()); - env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); - env->DeleteLocalRef(jUrl); -} void MediaPlayerPrivate::prepareToPlay() { // We are about to start playing. Since our Java VideoView cannot @@ -237,45 +122,6 @@ void MediaPlayerPrivate::prepareToPlay() { m_player->readyStateChanged(); } -void MediaPlayerPrivate::paint(GraphicsContext* ctxt, const IntRect& r) -{ - if (ctxt->paintingDisabled()) - return; - - if (!m_isVisible) - return; - - if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef())) - return; - - SkCanvas* canvas = ctxt->platformContext()->mCanvas; - // We paint with the following rules in mind: - // - only downscale the poster, never upscale - // - maintain the natural aspect ratio of the poster - // - the poster should be centered in the target rect - float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height()); - int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width(); - int posterHeight = posterWidth / originalRatio; - int posterX = ((r.width() - posterWidth) / 2) + r.x(); - int posterY = ((r.height() - posterHeight) / 2) + r.y(); - IntRect targetRect(posterX, posterY, posterWidth, posterHeight); - canvas->drawBitmapRect(*m_poster, 0, targetRect, 0); -} - -MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) -{ - return new MediaPlayerPrivate(player); -} - -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) -{ -} - -MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) -{ - return MediaPlayer::IsNotSupported; -} - MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player), m_glue(0), @@ -290,72 +136,6 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) m_naturalSizeUnknown(true), m_isVisible(false) { - JNIEnv* env = JSC::Bindings::getJNIEnv(); - if (!env) - return; - - jclass clazz = env->FindClass(g_ProxyJavaClass); - if (!clazz) - return; - - m_glue = new JavaGlue; - m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;"); - m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V"); - m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V"); - m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V"); - m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V"); - m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V"); - m_glue->m_javaProxy = NULL; - env->DeleteLocalRef(clazz); - // An exception is raised if any of the above fails. - checkException(env); -} - -void MediaPlayerPrivate::createJavaPlayerIfNeeded() -{ - // Check if we have been already created. - if (m_glue->m_javaProxy) - return; - - FrameView* frameView = m_player->frameView(); - if (!frameView) - return; - - JNIEnv* env = JSC::Bindings::getJNIEnv(); - if (!env) - return; - - jclass clazz = env->FindClass(g_ProxyJavaClass); - if (!clazz) - return; - - WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView); - ASSERT(webViewCore); - - // Get the HTML5VideoViewProxy instance - jobject obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this); - m_glue->m_javaProxy = env->NewGlobalRef(obj); - // Send the poster - jstring jUrl = 0; - if (m_posterUrl.length()) - jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length()); - // Sending a NULL jUrl allows the Java side to try to load the default poster. - env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); - if (jUrl) - env->DeleteLocalRef(jUrl); - // Clean up. - env->DeleteLocalRef(obj); - env->DeleteLocalRef(clazz); - checkException(env); -} - -void MediaPlayerPrivate::onPrepared(int duration, int width, int height) { - m_duration = duration / 1000.0f; - m_naturalSize = IntSize(width, height); - m_naturalSizeUnknown = false; - m_hasVideo = true; - m_player->durationChanged(); - m_player->sizeChanged(); } void MediaPlayerPrivate::onEnded() { @@ -368,25 +148,269 @@ void MediaPlayerPrivate::onEnded() { m_readyState = MediaPlayer::HaveNothing; } -void MediaPlayerPrivate::onPosterFetched(SkBitmap* poster) { - m_poster = poster; - if (m_naturalSizeUnknown) { - // We had to fake the size at startup, or else our paint - // method would not be called. If we haven't yet received - // the onPrepared event, update the intrinsic size to the size - // of the poster. That will be overriden when onPrepare comes. - // In case of an error, we should report the poster size, rather - // than our initial fake value. - m_naturalSize = IntSize(poster->width(), poster->height()); - m_player->sizeChanged(); - } -} - void MediaPlayerPrivate::onTimeupdate(int position) { m_currentTime = position / 1000.0f; m_player->timeChanged(); } +class MediaPlayerVideoPrivate : public MediaPlayerPrivate { +public: + void load(const String& url) { m_url = url; } + void play() { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length() || !m_glue->m_javaProxy) + return; + + m_paused = false; + jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length()); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl); + env->DeleteLocalRef(jUrl); + + checkException(env); + } + bool canLoadPoster() const { return true; } + void setPoster(const String& url) { + m_posterUrl = url; + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_glue->m_javaProxy || !m_posterUrl.length()) + return; + // Send the poster + jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length()); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); + env->DeleteLocalRef(jUrl); + } + void paint(GraphicsContext* ctxt, const IntRect& r) { + if (ctxt->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef())) + return; + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + // We paint with the following rules in mind: + // - only downscale the poster, never upscale + // - maintain the natural aspect ratio of the poster + // - the poster should be centered in the target rect + float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height()); + int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width(); + int posterHeight = posterWidth / originalRatio; + int posterX = ((r.width() - posterWidth) / 2) + r.x(); + int posterY = ((r.height() - posterHeight) / 2) + r.y(); + IntRect targetRect(posterX, posterY, posterWidth, posterHeight); + canvas->drawBitmapRect(*m_poster, 0, targetRect, 0); + } + + void onPosterFetched(SkBitmap* poster) { + m_poster = poster; + if (m_naturalSizeUnknown) { + // We had to fake the size at startup, or else our paint + // method would not be called. If we haven't yet received + // the onPrepared event, update the intrinsic size to the size + // of the poster. That will be overriden when onPrepare comes. + // In case of an error, we should report the poster size, rather + // than our initial fake value. + m_naturalSize = IntSize(poster->width(), poster->height()); + m_player->sizeChanged(); + } + } + + void onPrepared(int duration, int width, int height) { + m_duration = duration / 1000.0f; + m_naturalSize = IntSize(width, height); + m_naturalSizeUnknown = false; + m_hasVideo = true; + m_player->durationChanged(); + m_player->sizeChanged(); + } + + bool hasAudio() { return false; } // do not display the audio UI + bool hasVideo() { return m_hasVideo; } + + MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClass); + + if (!clazz) + return; + + m_glue = new JavaGlue; + m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;"); + m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V"); + m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V"); + + m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V"); + m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V"); + m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V"); + m_glue->m_javaProxy = NULL; + env->DeleteLocalRef(clazz); + // An exception is raised if any of the above fails. + checkException(env); + } + + void createJavaPlayerIfNeeded() { + // Check if we have been already created. + if (m_glue->m_javaProxy) + return; + + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClass); + + if (!clazz) + return; + + jobject obj = NULL; + + FrameView* frameView = m_player->frameView(); + if (!frameView) + return; + WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView); + ASSERT(webViewCore); + + // Get the HTML5VideoViewProxy instance + obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this); + m_glue->m_javaProxy = env->NewGlobalRef(obj); + // Send the poster + jstring jUrl = 0; + if (m_posterUrl.length()) + jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length()); + // Sending a NULL jUrl allows the Java side to try to load the default poster. + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); + if (jUrl) + env->DeleteLocalRef(jUrl); + + // Clean up. + if (obj) + env->DeleteLocalRef(obj); + env->DeleteLocalRef(clazz); + checkException(env); + } +}; + +class MediaPlayerAudioPrivate : public MediaPlayerPrivate { +public: + void load(const String& url) { + m_url = url; + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length()) + return; + + createJavaPlayerIfNeeded(); + + if (!m_glue->m_javaProxy) + return; + + jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length()); + // start loading the data asynchronously + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl); + env->DeleteLocalRef(jUrl); + checkException(env); + } + + void play() { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length()) + return; + + createJavaPlayerIfNeeded(); + + if (!m_glue->m_javaProxy) + return; + + m_paused = false; + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play); + checkException(env); + } + + bool hasAudio() { return true; } + + float maxTimeSeekable() const { + if (m_glue->m_javaProxy) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (env) { + float maxTime = env->CallFloatMethod(m_glue->m_javaProxy, + m_glue->m_getMaxTimeSeekable); + checkException(env); + return maxTime; + } + } + return 0; + } + + MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClassAudio); + + if (!clazz) + return; + + m_glue = new JavaGlue; + m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V"); + m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V"); + m_glue->m_play = env->GetMethodID(clazz, "play", "()V"); + m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F"); + m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V"); + m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V"); + m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V"); + m_glue->m_javaProxy = NULL; + env->DeleteLocalRef(clazz); + // An exception is raised if any of the above fails. + checkException(env); + } + + void createJavaPlayerIfNeeded() { + // Check if we have been already created. + if (m_glue->m_javaProxy) + return; + + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClassAudio); + + if (!clazz) + return; + + jobject obj = NULL; + + // Get the HTML5Audio instance + obj = env->NewObject(clazz, m_glue->m_newInstance, this); + m_glue->m_javaProxy = env->NewGlobalRef(obj); + + // Clean up. + if (obj) + env->DeleteLocalRef(obj); + env->DeleteLocalRef(clazz); + checkException(env); + } + + void onPrepared(int duration, int width, int height) { + m_duration = duration / 1000.0f; + m_player->durationChanged(); + m_player->sizeChanged(); + m_player->prepareToPlay(); + } +}; + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + if (player->mediaElementType() == MediaPlayer::Video) + return new MediaPlayerVideoPrivate(player); + return new MediaPlayerAudioPrivate(player); +} + } namespace android { @@ -416,6 +440,13 @@ static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointe player->onPosterFetched(posterNative); } +static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer) { + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + //TODO: player->onBuffering(percent); + } +} + static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer) { if (pointer) { WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); @@ -437,11 +468,28 @@ static JNINativeMethod g_MediaPlayerMethods[] = { (void*) OnTimeupdate }, }; -int register_mediaplayer(JNIEnv* env) +static JNINativeMethod g_MediaAudioPlayerMethods[] = { + { "nativeOnBuffering", "(II)V", + (void*) OnBuffering }, + { "nativeOnEnded", "(I)V", + (void*) OnEnded }, + { "nativeOnPrepared", "(IIII)V", + (void*) OnPrepared }, + { "nativeOnTimeupdate", "(II)V", + (void*) OnTimeupdate }, +}; + +int register_mediaplayer_video(JNIEnv* env) { return jniRegisterNativeMethods(env, g_ProxyJavaClass, g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods)); } +int register_mediaplayer_audio(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio, + g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods)); +} + } #endif // VIDEO diff --git a/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/WebKit/android/WebCoreSupport/PlatformBridge.cpp index 05aa42b..833deb5 100644 --- a/WebKit/android/WebCoreSupport/PlatformBridge.cpp +++ b/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -27,50 +27,21 @@ #include <PlatformBridge.h> #include "CookieClient.h" +#include "FileSystemClient.h" +#include "FrameView.h" #include "JavaSharedClient.h" #include "KeyGeneratorClient.h" #include "PluginView.h" +#include "WebCoreFrameBridge.h" #include "WebViewCore.h" #include "npruntime.h" #include <wtf/android/AndroidThreading.h> #include <wtf/MainThread.h> -#if USE(ACCELERATED_COMPOSITING) -#include "LayerAndroid.h" -#endif - using namespace android; namespace WebCore { -#if USE(ACCELERATED_COMPOSITING) - -void PlatformBridge::setUIRootLayer(const WebCore::FrameView* view, const LayerAndroid* layer) -{ - android::WebViewCore* core = android::WebViewCore::getWebViewCore(view); - core->setUIRootLayer(layer); -} - -void PlatformBridge::immediateRepaint(const WebCore::FrameView* view) -{ - android::WebViewCore* core = android::WebViewCore::getWebViewCore(view); - core->immediateRepaint(); -} - -#endif // USE(ACCELERATED_COMPOSITING) - -int PlatformBridge::screenWidth(const WebCore::FrameView* view) -{ - android::WebViewCore* core = android::WebViewCore::getWebViewCore(view); - return static_cast<int>((core->screenWidthScale() * core->screenWidth()) / core->scale()); -} - -int PlatformBridge::screenHeight(const WebCore::FrameView* view) -{ - android::WebViewCore* core = android::WebViewCore::getWebViewCore(view); - return core->screenHeight(); -} - WTF::Vector<String> PlatformBridge::getSupportedKeyStrengthList() { KeyGeneratorClient* client = JavaSharedClient::GetKeyGeneratorClient(); @@ -135,11 +106,28 @@ bool PlatformBridge::isWebViewPaused(const WebCore::FrameView* frameView) return webViewCore->isPaused(); } +bool PlatformBridge::canScroll(const WebCore::FrameView* frameView) +{ + // We want to ignore requests to scroll that were not initiated by the + // user. An example of this is when text is inserted into a + // textfield/area, which results in a scroll. We ignore this because + // we know how to do this ourselves in the UI thread. + // An example of it being initiated by the user is if the user clicks + // an anchor element which simply scrolls the page. + return android::WebFrame::getWebFrame(frameView->frame())->userInitiatedClick(); +} + bool PlatformBridge::popupsAllowed(NPP) { return false; } +String PlatformBridge::resolveFilePathForContentUri(const String& contentUri) +{ + FileSystemClient* client = JavaSharedClient::GetFileSystemClient(); + return client->resolveFilePathForContentUri(contentUri); +} + } // namespace WebCore diff --git a/WebKit/android/WebCoreSupport/V8Counters.cpp b/WebKit/android/WebCoreSupport/V8Counters.cpp index 7f3f3ad..7472447 100644 --- a/WebKit/android/WebCoreSupport/V8Counters.cpp +++ b/WebKit/android/WebCoreSupport/V8Counters.cpp @@ -33,9 +33,9 @@ #include "V8Counters.h" #include "NotImplemented.h" -#include <CString.h> #include <StringHash.h> #include <utils/Log.h> +#include <wtf/text/CString.h> namespace WebCore { diff --git a/WebKit/android/benchmark/Intercept.cpp b/WebKit/android/benchmark/Intercept.cpp index fe8e7bb..e5a98f1 100644 --- a/WebKit/android/benchmark/Intercept.cpp +++ b/WebKit/android/benchmark/Intercept.cpp @@ -27,7 +27,6 @@ #include "config.h" #include "Base64.h" -#include "CString.h" #include "HTTPParsers.h" #include "Intercept.h" #include "ResourceHandle.h" @@ -38,6 +37,7 @@ #include "TextEncoding.h" #include <utils/Log.h> #include <wtf/HashMap.h> +#include <wtf/text/CString.h> PassRefPtr<WebCore::ResourceLoaderAndroid> MyResourceLoader::create( ResourceHandle* handle, String url) diff --git a/WebKit/android/jni/JavaBridge.cpp b/WebKit/android/jni/JavaBridge.cpp index 0e65e1c..6aceb1c 100644 --- a/WebKit/android/jni/JavaBridge.cpp +++ b/WebKit/android/jni/JavaBridge.cpp @@ -31,6 +31,7 @@ #include "Cache.h" #include "Connection.h" #include "CookieClient.h" +#include "FileSystemClient.h" #include "JavaSharedClient.h" #include "KeyGeneratorClient.h" #include "KURL.h" @@ -62,7 +63,7 @@ static jfieldID gJavaBridge_ObjectID; // ---------------------------------------------------------------------------- -class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient +class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient, public FileSystemClient { public: JavaBridge(JNIEnv* env, jobject obj); @@ -84,6 +85,7 @@ public: virtual WTF::Vector<String> getSupportedKeyStrengthList(); virtual WebCore::String getSignedPublicKeyAndChallengeString(unsigned index, const WebCore::String& challenge, const WebCore::KURL& url); + virtual WebCore::String resolveFilePathForContentUri(const WebCore::String& uri); //////////////////////////////////////////// @@ -120,6 +122,7 @@ private: jmethodID mSignalFuncPtrQueue; jmethodID mGetKeyStrengthList; jmethodID mGetSignedPublicKey; + jmethodID mResolveFilePathForContentUri; }; static void (*sSharedTimerFiredCallback)(); @@ -139,6 +142,7 @@ JavaBridge::JavaBridge(JNIEnv* env, jobject obj) mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V"); mGetKeyStrengthList = env->GetMethodID(clazz, "getKeyStrengthList", "()[Ljava/lang/String;"); mGetSignedPublicKey = env->GetMethodID(clazz, "getSignedPublicKey", "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + mResolveFilePathForContentUri = env->GetMethodID(clazz, "resolveFilePathForContentUri", "(Ljava/lang/String;)Ljava/lang/String;"); LOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer"); LOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer"); @@ -154,6 +158,7 @@ JavaBridge::JavaBridge(JNIEnv* env, jobject obj) JavaSharedClient::SetCookieClient(this); JavaSharedClient::SetPluginClient(this); JavaSharedClient::SetKeyGeneratorClient(this); + JavaSharedClient::SetFileSystemClient(this); } JavaBridge::~JavaBridge() @@ -168,6 +173,7 @@ JavaBridge::~JavaBridge() JavaSharedClient::SetCookieClient(NULL); JavaSharedClient::SetPluginClient(NULL); JavaSharedClient::SetKeyGeneratorClient(NULL); + JavaSharedClient::SetFileSystemClient(NULL); } void @@ -308,6 +314,17 @@ WebCore::String JavaBridge::getSignedPublicKeyAndChallengeString(unsigned index, return ret; } +WebCore::String JavaBridge::resolveFilePathForContentUri(const WebCore::String& uri) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jstring jUri = env->NewString(uri.characters(), uri.length()); + AutoJObject obj = getRealObject(env, mJavaObject); + jstring path = static_cast<jstring>(env->CallObjectMethod(obj.get(), mResolveFilePathForContentUri, jUri)); + WebCore::String ret = to_string(env, path); + env->DeleteLocalRef(jUri); + env->DeleteLocalRef(path); + return ret; +} + // ---------------------------------------------------------------------------- void JavaBridge::Constructor(JNIEnv* env, jobject obj) diff --git a/WebKit/android/jni/JavaSharedClient.cpp b/WebKit/android/jni/JavaSharedClient.cpp index ce46570..e884c99 100644 --- a/WebKit/android/jni/JavaSharedClient.cpp +++ b/WebKit/android/jni/JavaSharedClient.cpp @@ -24,6 +24,7 @@ */ #include "config.h" +#include "FileSystemClient.h" #include "JavaSharedClient.h" #include "TimerClient.h" #include "SkDeque.h" @@ -50,6 +51,11 @@ namespace android { return gKeyGeneratorClient; } + FileSystemClient* JavaSharedClient::GetFileSystemClient() + { + return gFileSystemClient; + } + void JavaSharedClient::SetTimerClient(TimerClient* client) { gTimerClient = client; @@ -70,10 +76,16 @@ namespace android { gKeyGeneratorClient = client; } + void JavaSharedClient::SetFileSystemClient(FileSystemClient* client) + { + gFileSystemClient = client; + } + TimerClient* JavaSharedClient::gTimerClient = NULL; CookieClient* JavaSharedClient::gCookieClient = NULL; PluginClient* JavaSharedClient::gPluginClient = NULL; KeyGeneratorClient* JavaSharedClient::gKeyGeneratorClient = NULL; + FileSystemClient* JavaSharedClient::gFileSystemClient = NULL; /////////////////////////////////////////////////////////////////////////// diff --git a/WebKit/android/jni/JavaSharedClient.h b/WebKit/android/jni/JavaSharedClient.h index d33df67..9a09280 100644 --- a/WebKit/android/jni/JavaSharedClient.h +++ b/WebKit/android/jni/JavaSharedClient.h @@ -32,6 +32,7 @@ namespace android { class CookieClient; class PluginClient; class KeyGeneratorClient; + class FileSystemClient; class JavaSharedClient { @@ -40,11 +41,13 @@ namespace android { static CookieClient* GetCookieClient(); static PluginClient* GetPluginClient(); static KeyGeneratorClient* GetKeyGeneratorClient(); + static FileSystemClient* GetFileSystemClient(); static void SetTimerClient(TimerClient* client); static void SetCookieClient(CookieClient* client); static void SetPluginClient(PluginClient* client); static void SetKeyGeneratorClient(KeyGeneratorClient* client); + static void SetFileSystemClient(FileSystemClient* client); // can be called from any thread, to be executed in webkit thread static void EnqueueFunctionPtr(void (*proc)(void*), void* payload); @@ -56,6 +59,7 @@ namespace android { static CookieClient* gCookieClient; static PluginClient* gPluginClient; static KeyGeneratorClient* gKeyGeneratorClient; + static FileSystemClient* gFileSystemClient; }; } #endif diff --git a/WebKit/android/jni/MIMETypeRegistry.cpp b/WebKit/android/jni/MIMETypeRegistry.cpp index 32f387d..5fe9ccd 100644 --- a/WebKit/android/jni/MIMETypeRegistry.cpp +++ b/WebKit/android/jni/MIMETypeRegistry.cpp @@ -58,4 +58,9 @@ String MIMETypeRegistry::getMIMETypeForExtension(const String& ext) return result; } +bool MIMETypeRegistry::isApplicationPluginMIMEType(const String&) +{ + return false; +} + } diff --git a/WebKit/android/jni/MockGeolocation.cpp b/WebKit/android/jni/MockGeolocation.cpp index df580c3..1c236c3 100755 --- a/WebKit/android/jni/MockGeolocation.cpp +++ b/WebKit/android/jni/MockGeolocation.cpp @@ -53,7 +53,7 @@ static void setPosition(JNIEnv* env, jobject, double latitude, double longitude, false, 0.0, // altitudeAccuracy, false, 0.0, // heading false, 0.0); // speed - RefPtr<Geoposition> position = Geoposition::create(coordinates.release(), WTF::currentTime()); + RefPtr<Geoposition> position = Geoposition::create(coordinates.release(), WTF::currentTimeMS()); GeolocationServiceMock::setPosition(position.release()); } diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp index 250ffc9..bfd4b62 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -32,7 +32,6 @@ #include "AtomicString.h" #include "BackForwardList.h" #include "Cache.h" -#include "CString.h" #include "Chrome.h" #include "ChromeClientAndroid.h" #include "ContextMenuClientAndroid.h" @@ -74,6 +73,7 @@ #include "SecurityOrigin.h" #include "SelectionController.h" #include "Settings.h" +#include "StringBuilder.h" #include "SubstituteData.h" #include "WebCoreJni.h" #include "WebCoreResourceLoader.h" @@ -93,6 +93,7 @@ #include <utils/AssetManager.h> #include <wtf/CurrentTime.h> #include <wtf/Platform.h> +#include <wtf/text/CString.h> #if USE(JSC) #include "GCController.h" @@ -111,11 +112,16 @@ #include "TimeCounter.h" #endif +#if ENABLE(ARCHIVE) +#include "WebArchiveAndroid.h" +#endif + using namespace JSC::Bindings; static String* gUploadFileLabel; static String* gResetLabel; static String* gSubmitLabel; +static String* gNoFileChosenLabel; String* WebCore::PlatformBridge::globalLocalizedName( WebCore::PlatformBridge::rawResId resId) @@ -127,6 +133,9 @@ String* WebCore::PlatformBridge::globalLocalizedName( return gResetLabel; case WebCore::PlatformBridge::SubmitLabel: return gSubmitLabel; + case WebCore::PlatformBridge::FileUploadNoFileChosenLabel: + return gNoFileChosenLabel; + default: return 0; } @@ -148,6 +157,9 @@ void initGlobalLocalizedName(WebCore::PlatformBridge::rawResId resId, case WebCore::PlatformBridge::SubmitLabel: pointer = &gSubmitLabel; break; + case WebCore::PlatformBridge::FileUploadNoFileChosenLabel: + pointer = &gNoFileChosenLabel; + break; default: return; } @@ -310,9 +322,9 @@ static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHead jstring val = env->NewString((unsigned short *)i->second.characters(), i->second.length()); if (key && val) { env->CallObjectMethod(hashMap, put, key, val); - env->DeleteLocalRef(key); - env->DeleteLocalRef(val); } + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); } env->DeleteLocalRef(mapClass); @@ -320,19 +332,6 @@ static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHead return hashMap; } -// In WebViewCore.java, we artificially append the filename to the URI so that -// webkit treats the actual display name of the file as the filename, rather -// than the last segment of the URI (which will simply be a number). When we -// pass the URI up to BrowserFrame, we no longer need the appended name (in fact -// it causes problems), so remove it here. -// FIXME: If we rewrite pathGetFileName (the current version is in -// FileSystemPOSIX), we can get the filename that way rather than appending it. -static jstring uriFromUriFileName(JNIEnv* env, const WebCore::String& name) -{ - const WebCore::String fileName = name.left(name.reverseFind('/')); - return env->NewString(fileName.characters(), fileName.length()); -} - // This class stores the URI and the size of each file for upload. The URI is // stored so we do not have to create it again. The size is stored so we can // compare the actual size of the file with the stated size. If the actual size @@ -341,7 +340,7 @@ static jstring uriFromUriFileName(JNIEnv* env, const WebCore::String& name) class FileInfo { public: FileInfo(JNIEnv* env, const WebCore::String& name) { - m_uri = uriFromUriFileName(env, name); + m_uri = env->NewString(name.characters(), name.length()); checkException(env); m_size = 0; m_env = env; @@ -722,6 +721,7 @@ WebFrame::didReceiveTouchIconURL(const WebCore::String& url, bool precomposed) env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidReceiveTouchIconUrl, jUrlStr, precomposed); + env->DeleteLocalRef(jUrlStr); checkException(env); } @@ -736,6 +736,7 @@ WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload) jstring jUrlStr = env->NewString((unsigned short*)urlStr.characters(), urlStr.length()); env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload); + env->DeleteLocalRef(jUrlStr); checkException(env); } @@ -765,6 +766,7 @@ WebFrame::canHandleRequest(const WebCore::ResourceRequest& request) // if browser app handles the url, we will return false to bail out WebCore loading jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), mJavaFrame->mHandleUrl, jUrlStr); checkException(env); + env->DeleteLocalRef(jUrlStr); return (ret == 0); } @@ -882,7 +884,8 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss dragC, inspectorC, 0, // PluginHalterClient - 0); // GeolocationControllerClient + 0, // GeolocationControllerClient + 0); // DeviceOrientationClient // css files without explicit MIMETYPE is treated as generic text files in // the Java side. So we can't enforce CSS MIMETYPE. page->settings()->setEnforceCSSMIMETypeInStrictMode(false); @@ -940,7 +943,7 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss WebCore::RenderSkinAndroid::Init(am, directory); } for (int i = WebCore::PlatformBridge::FileUploadLabel; - i <= WebCore::PlatformBridge::SubmitLabel; i++) + i <= WebCore::PlatformBridge::FileUploadNoFileChosenLabel; i++) initGlobalLocalizedName( static_cast<WebCore::PlatformBridge::rawResId>(i), webFrame); } @@ -1096,6 +1099,91 @@ static void StopLoading(JNIEnv *env, jobject obj) pFrame->loader()->stopForUserCancel(); } +#if ENABLE(ARCHIVE) +static String saveArchiveAutoname(String basename, String name, String extension) { + if (name.isNull() || name.isEmpty()) { + name = String("index"); + } + + String testname = basename; + testname.append(name); + testname.append(extension); + + errno = 0; + struct stat permissions; + if (stat(testname.utf8().data(), &permissions) < 0) { + if (errno == ENOENT) + return testname; + return String(); + } + + const int maxAttempts = 100; + for (int i = 1; i < maxAttempts; i++) { + String testname = basename; + testname.append(name); + testname.append("-"); + testname.append(String::number(i)); + testname.append(extension); + + errno = 0; + if (stat(testname.utf8().data(), &permissions) < 0) { + if (errno == ENOENT) + return testname; + return String(); + } + } + + return String(); +} +#endif + +static jobject SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboolean autoname) +{ +#if ENABLE(ARCHIVE) + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeSaveWebArchive must take a valid frame pointer!"); + + const char* basenameNative = getCharactersFromJStringInEnv(env, basename); + String basenameString = String::fromUTF8(basenameNative); + String filename; + + if (autoname) { + String name = pFrame->loader()->documentLoader()->originalURL().lastPathComponent(); + String extension = String(".webarchivexml"); + filename = saveArchiveAutoname(basenameString, name, extension); + } else { + filename = basenameString; + } + + if (filename.isNull() || filename.isEmpty()) { + LOGD("saveWebArchive: Failed to select a filename to save."); + releaseCharactersForJStringInEnv(env, basename, basenameNative); + return JNI_FALSE; + } + + const int noCompression = 0; + xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.utf8().data(), noCompression); + if (writer == NULL) { + LOGD("saveWebArchive: Failed to initialize xml writer."); + releaseCharactersForJStringInEnv(env, basename, basenameNative); + return JNI_FALSE; + } + + RefPtr<WebArchiveAndroid> archive = WebCore::WebArchiveAndroid::create(pFrame); + + bool result = archive->saveWebArchive(writer); + + releaseCharactersForJStringInEnv(env, basename, basenameNative); + xmlFreeTextWriter(writer); + + if (result) { + return env->NewStringUTF(filename.utf8().data()); + } + + return NULL; +#endif +} + static jstring ExternalRepresentation(JNIEnv *env, jobject obj) { #ifdef ANDROID_INSTRUMENT @@ -1112,6 +1200,28 @@ static jstring ExternalRepresentation(JNIEnv *env, jobject obj) return env->NewString(renderDump.characters(), len); } +static WebCore::StringBuilder FrameAsText(WebCore::Frame *pFrame, jboolean dumpChildFrames) { + WebCore::StringBuilder renderDump; + if (!pFrame) + return renderDump; + WebCore::Element *documentElement = pFrame->document()->documentElement(); + if (!documentElement) + return renderDump; + if (pFrame->tree()->parent()) { + renderDump.append("\n--------\nFrame: '"); + renderDump.append(pFrame->tree()->name()); + renderDump.append("'\n--------\n"); + } + renderDump.append(((WebCore::HTMLElement*)documentElement)->innerText()); + renderDump.append("\n"); + if (dumpChildFrames) { + for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) { + renderDump.append(FrameAsText(pFrame->tree()->child(i), dumpChildFrames).toString()); + } + } + return renderDump; +} + static jstring DocumentAsText(JNIEnv *env, jobject obj) { #ifdef ANDROID_INSTRUMENT @@ -1120,11 +1230,26 @@ static jstring DocumentAsText(JNIEnv *env, jobject obj) WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!"); - WebCore::Element *documentElement = pFrame->document()->documentElement(); - if (!documentElement) + WebCore::String renderDump = FrameAsText(pFrame, false /* dumpChildFrames */).toString(); + unsigned len = renderDump.length(); + if (!len) return NULL; - WebCore::String renderDump = ((WebCore::HTMLElement*)documentElement)->innerText(); - renderDump.append("\n"); + return env->NewString((unsigned short*)renderDump.characters(), len); +} + +static jstring ChildFramesAsText(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!"); + + WebCore::StringBuilder renderDumpBuilder; + for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) { + renderDumpBuilder.append(FrameAsText(pFrame->tree()->child(i), true /* dumpChildFrames */).toString()); + } + WebCore::String renderDump = renderDumpBuilder.toString(); unsigned len = renderDump.length(); if (!len) return NULL; @@ -1413,8 +1538,8 @@ static jboolean HasPasswordField(JNIEnv *env, jobject obj) // class, but just normal Element class. while (node && !found && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty()) { - WTF::Vector<WebCore::HTMLFormControlElement*>& elements = - ((WebCore::HTMLFormElement*)node)->formElements; + const WTF::Vector<WebCore::HTMLFormControlElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); size_t size = elements.size(); for (size_t i = 0; i< size && !found; i++) { WebCore::HTMLFormControlElement* e = elements[i]; @@ -1444,8 +1569,8 @@ static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj) WebCore::Node* node = form->firstItem(); while (node && !found && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty()) { - WTF::Vector<WebCore::HTMLFormControlElement*>& elements = - ((WebCore::HTMLFormElement*)node)->formElements; + const WTF::Vector<WebCore::HTMLFormControlElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); size_t size = elements.size(); for (size_t i = 0; i< size && !found; i++) { WebCore::HTMLFormControlElement* e = elements[i]; @@ -1455,7 +1580,7 @@ static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj) continue; if (input->inputType() == WebCore::HTMLInputElement::PASSWORD) password = input->value(); - else if (input->inputType() == WebCore::HTMLInputElement::TEXT) + else if (input->inputType() == WebCore::HTMLInputElement::TEXT || input->inputType() == WebCore::HTMLInputElement::EMAIL) username = input->value(); if (!username.isNull() && !password.isNull()) found = true; @@ -1490,8 +1615,8 @@ static void SetUsernamePassword(JNIEnv *env, jobject obj, WebCore::Node* node = form->firstItem(); while (node && !found && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty()) { - WTF::Vector<WebCore::HTMLFormControlElement*>& elements = - ((WebCore::HTMLFormElement*)node)->formElements; + const WTF::Vector<WebCore::HTMLFormControlElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); size_t size = elements.size(); for (size_t i = 0; i< size && !found; i++) { WebCore::HTMLFormControlElement* e = elements[i]; @@ -1501,7 +1626,7 @@ static void SetUsernamePassword(JNIEnv *env, jobject obj, continue; if (input->inputType() == WebCore::HTMLInputElement::PASSWORD) passwordEle = input; - else if (input->inputType() == WebCore::HTMLInputElement::TEXT) + else if (input->inputType() == WebCore::HTMLInputElement::TEXT || input->inputType() == WebCore::HTMLInputElement::EMAIL) usernameEle = input; if (usernameEle != NULL && passwordEle != NULL) found = true; @@ -1543,7 +1668,7 @@ static jobject GetFormTextData(JNIEnv *env, jobject obj) node = collection->nextItem()) { form = static_cast<WebCore::HTMLFormElement*>(node); if (form->autoComplete()) { - WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->formElements; + 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]; @@ -1604,10 +1729,14 @@ static JNINativeMethod gBrowserFrameNativeMethods[] = { (void*) PostUrl }, { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void*) LoadData }, + { "nativeSaveWebArchive", "(Ljava/lang/String;Z)Ljava/lang/String;", + (void*) SaveWebArchive }, { "externalRepresentation", "()Ljava/lang/String;", (void*) ExternalRepresentation }, { "documentAsText", "()Ljava/lang/String;", (void*) DocumentAsText }, + { "childFramesAsText", "()Ljava/lang/String;", + (void*) ChildFramesAsText }, { "reload", "(Z)V", (void*) Reload }, { "nativeGoBackOrForward", "(I)V", diff --git a/WebKit/android/jni/WebCoreJniOnLoad.cpp b/WebKit/android/jni/WebCoreJniOnLoad.cpp index b5bf9dd..1a3c676 100644 --- a/WebKit/android/jni/WebCoreJniOnLoad.cpp +++ b/WebKit/android/jni/WebCoreJniOnLoad.cpp @@ -82,7 +82,8 @@ extern int register_webstorage(JNIEnv*); extern int register_geolocation_permissions(JNIEnv*); extern int register_mock_geolocation(JNIEnv*); #if ENABLE(VIDEO) -extern int register_mediaplayer(JNIEnv*); +extern int register_mediaplayer_audio(JNIEnv*); +extern int register_mediaplayer_video(JNIEnv*); #endif } @@ -107,7 +108,8 @@ static RegistrationMethod gWebCoreRegMethods[] = { { "GeolocationPermissions", android::register_geolocation_permissions }, { "MockGeolocation", android::register_mock_geolocation }, #if ENABLE(VIDEO) - { "HTML5VideoViewProxy", android::register_mediaplayer }, + { "HTML5Audio", android::register_mediaplayer_audio }, + { "HTML5VideoViewProxy", android::register_mediaplayer_video }, #endif }; @@ -192,7 +194,8 @@ EXPORT void benchmark(const char* url, int reloadCount, int width, int height) { new DragClientAndroid, new InspectorClientAndroid, 0, // PluginHalterClient - 0); // GeolocationControllerClient + 0, // GeolocationControllerClient + 0); // DeviceOrientationClient editor->setPage(page); // Create MyWebFrame that intercepts network requests diff --git a/WebKit/android/jni/WebCoreResourceLoader.cpp b/WebKit/android/jni/WebCoreResourceLoader.cpp index 297ecb0..32e8cd8 100644 --- a/WebKit/android/jni/WebCoreResourceLoader.cpp +++ b/WebKit/android/jni/WebCoreResourceLoader.cpp @@ -28,7 +28,6 @@ #include "config.h" #include "WebCoreResourceLoader.h" -#include "CString.h" #include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceHandleClient.h" @@ -46,6 +45,7 @@ #include <stdlib.h> #include <utils/misc.h> #include <wtf/Platform.h> +#include <wtf/text/CString.h> namespace android { diff --git a/WebKit/android/jni/WebCoreViewBridge.h b/WebKit/android/jni/WebCoreViewBridge.h index 61990af..59e1c9a 100644 --- a/WebKit/android/jni/WebCoreViewBridge.h +++ b/WebKit/android/jni/WebCoreViewBridge.h @@ -38,20 +38,22 @@ namespace WebCore class WebCoreViewBridge : public WebCoreRefObject { public: - WebCoreViewBridge() : - mBounds(0,0,0,0), - m_windowBounds(0,0,0,0) - {} - virtual ~WebCoreViewBridge() {} + WebCoreViewBridge() { } + virtual ~WebCoreViewBridge() { } virtual void draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) = 0; const WebCore::IntRect& getBounds() const { - return mBounds; + return m_bounds; } - + + const WebCore::IntRect& getVisibleBounds() const + { + return m_visibleBounds; + } + const WebCore::IntRect& getWindowBounds() const { return m_windowBounds; @@ -59,14 +61,22 @@ public: void setSize(int w, int h) { - mBounds.setWidth(w); - mBounds.setHeight(h); + m_bounds.setWidth(w); + m_bounds.setHeight(h); + } + + void setVisibleSize(int w, int h) + { + m_visibleBounds.setWidth(w); + m_visibleBounds.setHeight(h); } void setLocation(int x, int y) { - mBounds.setX(x); - mBounds.setY(y); + m_bounds.setX(x); + m_bounds.setY(y); + m_visibleBounds.setX(x); + m_visibleBounds.setY(y); } void setWindowBounds(int x, int y, int h, int v) @@ -74,17 +84,23 @@ public: m_windowBounds = WebCore::IntRect(x, y, h, v); } - int width() const { return mBounds.width(); } - int height() const { return mBounds.height(); } - int locX() const { return mBounds.x(); } - int locY() const { return mBounds.y(); } + int width() const { return m_bounds.width(); } + int height() const { return m_bounds.height(); } + int locX() const { return m_bounds.x(); } + int locY() const { return m_bounds.y(); } + + int visibleWidth() const { return m_visibleBounds.width(); } + int visibleHeight() const { return m_visibleBounds.height(); } + int visibleX() const { return m_visibleBounds.x(); } + int visibleY() const { return m_visibleBounds.y(); } virtual bool forFrameView() const { return false; } virtual bool forPluginView() const { return false; } private: - WebCore::IntRect mBounds; + WebCore::IntRect m_bounds; WebCore::IntRect m_windowBounds; + WebCore::IntRect m_visibleBounds; }; #endif // WEBCORE_VIEW_BRIDGE_H diff --git a/WebKit/android/jni/WebHistory.cpp b/WebKit/android/jni/WebHistory.cpp index d518ba8..37a4d1d 100644 --- a/WebKit/android/jni/WebHistory.cpp +++ b/WebKit/android/jni/WebHistory.cpp @@ -29,7 +29,6 @@ #include "WebHistory.h" #include "BackForwardList.h" -#include "CString.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" @@ -49,6 +48,7 @@ #include <utils/misc.h> #include <wtf/OwnPtr.h> #include <wtf/Platform.h> +#include <wtf/text/CString.h> namespace android { @@ -419,9 +419,9 @@ static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item) const int scale = bridge->scale(); LOGV("Writing scale %d", scale); v.append((char*)&scale, sizeof(int)); - const int screenWidthScale = bridge->screenWidthScale(); - LOGV("Writing screen width scale %d", screenWidthScale); - v.append((char*)&screenWidthScale, sizeof(int)); + const int textWrapScale = bridge->textWrapScale(); + LOGV("Writing text wrap scale %d", textWrapScale); + v.append((char*)&textWrapScale, sizeof(int)); // Document state const WTF::Vector<WebCore::String>& docState = item->documentState(); @@ -603,8 +603,8 @@ static bool read_item_recursive(WebCore::HistoryItem* newItem, bridge->setScale(l); data += sizeofUnsigned; memcpy(&l, data, sizeofUnsigned); - LOGV("Screen width scale %d", l); - bridge->setScreenWidthScale(l); + LOGV("Text wrap scale %d", l); + bridge->setTextWrapScale(l); data += sizeofUnsigned; if (end - data < sizeofUnsigned) diff --git a/WebKit/android/jni/WebHistory.h b/WebKit/android/jni/WebHistory.h index 12bf00a..2d86aa4 100644 --- a/WebKit/android/jni/WebHistory.h +++ b/WebKit/android/jni/WebHistory.h @@ -44,9 +44,9 @@ public: static void UpdateHistoryIndex(const AutoJObject&, int); }; -// there are two scale factors saved with each history item. mScale reflects the -// viewport scale factor, default to 100 means 100%. mScreenWidthScale records -// the scale factor for the screen width used to wrap the text paragraph. +// there are two scale factors saved with each history item. m_scale reflects the +// viewport scale factor, default to 100 means 100%. m_textWrapScale records +// the scale factor for wrapping the text paragraph. class WebHistoryItem : public WebCore::AndroidWebHistoryBridge { public: WebHistoryItem(WebHistoryItem* parent) diff --git a/WebKit/android/jni/WebIconDatabase.cpp b/WebKit/android/jni/WebIconDatabase.cpp index 840d161..6a20f6f 100644 --- a/WebKit/android/jni/WebIconDatabase.cpp +++ b/WebKit/android/jni/WebIconDatabase.cpp @@ -38,6 +38,7 @@ #include <JNIHelp.h> #include <JNIUtility.h> +#include <SharedBuffer.h> #include <SkBitmap.h> #include <SkImageDecoder.h> #include <SkTemplates.h> diff --git a/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp index 70ecded..d9a7cf0 100644 --- a/WebKit/android/jni/WebSettings.cpp +++ b/WebKit/android/jni/WebSettings.cpp @@ -29,7 +29,9 @@ #include <wtf/Platform.h> #include "ApplicationCacheStorage.h" +#include "BitmapAllocatorAndroid.h" #include "DatabaseTracker.h" +#include "Database.h" #include "DocLoader.h" #include "Document.h" #include "Frame.h" @@ -107,11 +109,13 @@ struct FieldIds { #endif mGeolocationEnabled = env->GetFieldID(clazz, "mGeolocationEnabled", "Z"); mGeolocationDatabasePath = env->GetFieldID(clazz, "mGeolocationDatabasePath", "Ljava/lang/String;"); + mXSSAuditorEnabled = env->GetFieldID(clazz, "mXSSAuditorEnabled", "Z"); mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz, "mJavaScriptCanOpenWindowsAutomatically", "Z"); mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z"); mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z"); mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z"); + mMaximumDecodedImageSize = env->GetFieldID(clazz, "mMaximumDecodedImageSize", "J"); mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z"); mPageCacheCapacity = env->GetFieldID(clazz, "mPageCacheCapacity", "I"); @@ -148,6 +152,7 @@ struct FieldIds { LOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport"); LOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows"); LOG_ASSERT(mShrinksStandaloneImagesToFit, "Could not find field mShrinksStandaloneImagesToFit"); + LOG_ASSERT(mMaximumDecodedImageSize, "Could not find field mMaximumDecodedImageSize"); LOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree"); LOG_ASSERT(mPageCacheCapacity, "Could not find field mPageCacheCapacity"); @@ -193,6 +198,7 @@ struct FieldIds { jfieldID mUseWideViewport; jfieldID mSupportMultipleWindows; jfieldID mShrinksStandaloneImagesToFit; + jfieldID mMaximumDecodedImageSize; jfieldID mUseDoubleTree; jfieldID mPageCacheCapacity; // Ordinal() method and value field for enums @@ -207,6 +213,7 @@ struct FieldIds { #endif jfieldID mGeolocationEnabled; jfieldID mGeolocationDatabasePath; + jfieldID mXSSAuditorEnabled; #if ENABLE(DATABASE) || ENABLE(DOM_STORAGE) jfieldID mDatabasePath; jfieldID mDatabasePathHasBeenSet; @@ -260,8 +267,8 @@ public: #endif jobject textSize = env->GetObjectField(obj, gFieldIds->mTextSize); float zoomFactor = env->GetIntField(textSize, gFieldIds->mTextSizeValue) / 100.0f; - if (pFrame->zoomFactor() != zoomFactor) - pFrame->setZoomFactor(zoomFactor, /*isTextOnly*/true); + if (pFrame->view()->zoomFactor() != zoomFactor) + pFrame->view()->setZoomFactor(zoomFactor, WebCore::ZoomTextOnly); jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily); s->setStandardFontFamily(to_string(env, str)); @@ -352,9 +359,13 @@ public: #endif flag = env->GetBooleanField(obj, gFieldIds->mShrinksStandaloneImagesToFit); s->setShrinksStandaloneImagesToFit(flag); + jlong maxImage = env->GetIntField(obj, gFieldIds->mMaximumDecodedImageSize); + if (maxImage == 0) + maxImage = computeMaxBitmapSizeForCache(); + s->setMaximumDecodedImageSize(maxImage); #if ENABLE(DATABASE) flag = env->GetBooleanField(obj, gFieldIds->mDatabaseEnabled); - s->setDatabasesEnabled(flag); + WebCore::Database::setIsAvailable(flag); flag = env->GetBooleanField(obj, gFieldIds->mDatabasePathHasBeenSet); if (flag) { @@ -384,6 +395,9 @@ public: WebCore::GeolocationPositionCache::setDatabasePath(to_string(env,str)); } + flag = env->GetBooleanField(obj, gFieldIds->mXSSAuditorEnabled); + s->setXSSAuditorEnabled(flag); + size = env->GetIntField(obj, gFieldIds->mPageCacheCapacity); if (size > 0) { s->setUsesPageCache(true); diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp index 1562775..d3e32d3 100644 --- a/WebKit/android/jni/WebViewCore.cpp +++ b/WebKit/android/jni/WebViewCore.cpp @@ -29,6 +29,7 @@ #include "WebViewCore.h" #include "AtomicString.h" +#include "BaseLayerAndroid.h" #include "CachedNode.h" #include "CachedRoot.h" #include "Chrome.h" @@ -37,6 +38,7 @@ #include "DatabaseTracker.h" #include "Document.h" #include "DOMWindow.h" +#include "DOMSelection.h" #include "Element.h" #include "Editor.h" #include "EditorClientAndroid.h" @@ -65,9 +67,9 @@ #include "HTMLSelectElement.h" #include "HTMLTextAreaElement.h" #include "HistoryItem.h" +#include "HitTestRequest.h" #include "HitTestResult.h" #include "InlineTextBox.h" -#include "KeyboardCodes.h" #include "Navigator.h" #include "Node.h" #include "NodeList.h" @@ -79,7 +81,9 @@ #include "PluginView.h" #include "Position.h" #include "ProgressTracker.h" +#include "Range.h" #include "RenderBox.h" +#include "RenderInline.h" #include "RenderLayer.h" #include "RenderPart.h" #include "RenderText.h" @@ -101,6 +105,7 @@ #include "TypingCommand.h" #include "WebCoreFrameBridge.h" #include "WebFrameView.h" +#include "WindowsKeyboardCodes.h" #include "android_graphics.h" #include <JNIHelp.h> @@ -109,9 +114,9 @@ #include <wtf/CurrentTime.h> #if USE(V8) -#include "CString.h" #include "ScriptController.h" #include "V8Counters.h" +#include <wtf/text/CString.h> #endif #if DEBUG_NAV_UI @@ -125,7 +130,7 @@ #ifdef ANDROID_DOM_LOGGING #include "AndroidLog.h" #include "RenderTreeAsText.h" -#include "CString.h" +#include <wtf/text/CString.h> FILE* gDomTreeFile = 0; FILE* gRenderTreeFile = 0; @@ -226,13 +231,10 @@ struct WebViewCore::JavaGlue { jmethodID m_updateViewport; jmethodID m_sendNotifyProgressFinished; jmethodID m_sendViewInvalidate; - jmethodID m_sendImmediateRepaint; - jmethodID m_setRootLayer; jmethodID m_updateTextfield; jmethodID m_updateTextSelection; jmethodID m_clearTextEntry; jmethodID m_restoreScale; - jmethodID m_restoreScreenWidthScale; jmethodID m_needTouchEvents; jmethodID m_requestKeyboard; jmethodID m_requestKeyboardWithSelection; @@ -253,6 +255,7 @@ struct WebViewCore::JavaGlue { jmethodID m_showRect; jmethodID m_centerFitRect; jmethodID m_setScrollbarModes; + jmethodID m_setInstallableWebApp; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } @@ -272,7 +275,6 @@ static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const Mutex WebViewCore::gFrameCacheMutex; Mutex WebViewCore::gButtonMutex; Mutex WebViewCore::gCursorBoundsMutex; -Mutex WebViewCore::m_contentMutex; WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe) : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired) @@ -289,8 +291,8 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_maxYScroll = 240/4; m_textGeneration = 0; m_screenWidth = 320; + m_textWrapWidth = 320; m_scale = 1; - m_screenWidthScale = 1; #if ENABLE(TOUCH_EVENTS) m_forwardingTouchEvents = false; #endif @@ -306,7 +308,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V"); m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V"); m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V"); - m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "()Ljava/lang/String;"); + m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;"); m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V"); m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z"); @@ -317,13 +319,10 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V"); m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V"); m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V"); - m_javaGlue->m_sendImmediateRepaint = GetJMethod(env, clazz, "sendImmediateRepaint", "()V"); - m_javaGlue->m_setRootLayer = GetJMethod(env, clazz, "setRootLayer", "(I)V"); m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V"); m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V"); m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V"); - m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V"); - m_javaGlue->m_restoreScreenWidthScale = GetJMethod(env, clazz, "restoreScreenWidthScale", "(I)V"); + m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(II)V"); m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V"); m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V"); m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V"); @@ -344,6 +343,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V"); m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V"); m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V"); + m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V"); env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this); @@ -485,7 +485,8 @@ void WebViewCore::recordPicture(SkPicture* picture) WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons); WebCore::GraphicsContext gc(&pgc); - view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, INT_MAX, INT_MAX)); + view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, + view->contentsWidth(), view->contentsHeight())); gButtonMutex.lock(); updateButtonList(&buttons); @@ -589,6 +590,9 @@ void WebViewCore::recordPictureSet(PictureSet* content) height = view->contentsHeight(); } + if (cacheBuilder().pictureSetDisabled()) + content->clear(); + content->checkDimensions(width, height, &m_addInval); // The inval region may replace existing pictures. The existing pictures @@ -741,50 +745,11 @@ void WebViewCore::updateCursorBounds(const CachedRoot* root, void WebViewCore::clearContent() { DBG_SET_LOG(""); - m_contentMutex.lock(); m_content.clear(); - m_contentMutex.unlock(); m_addInval.setEmpty(); m_rebuildInval.setEmpty(); } -void WebViewCore::copyContentToPicture(SkPicture* picture) -{ - DBG_SET_LOG("start"); - m_contentMutex.lock(); - PictureSet copyContent = PictureSet(m_content); - m_contentMutex.unlock(); - - int w = copyContent.width(); - int h = copyContent.height(); - copyContent.draw(picture->beginRecording(w, h, PICT_RECORD_FLAGS)); - picture->endRecording(); - DBG_SET_LOG("end"); -} - -bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewUIDrawTimeCounter); -#endif - DBG_SET_LOG("start"); - m_contentMutex.lock(); - PictureSet copyContent = PictureSet(m_content); - m_contentMutex.unlock(); - int sc = canvas->save(SkCanvas::kClip_SaveFlag); - SkRect clip; - clip.set(0, 0, copyContent.width(), copyContent.height()); - canvas->clipRect(clip, SkRegion::kDifference_Op); - canvas->drawColor(color); - canvas->restoreToCount(sc); - bool tookTooLong = copyContent.draw(canvas); - m_contentMutex.lock(); - m_content.setDrawTimes(copyContent); - m_contentMutex.unlock(); - DBG_SET_LOG("end"); - return tookTooLong; -} - bool WebViewCore::focusBoundsChanged() { bool result = m_focusBoundsChanged; @@ -792,18 +757,6 @@ bool WebViewCore::focusBoundsChanged() return result; } -bool WebViewCore::pictureReady() -{ - bool done; - m_contentMutex.lock(); - PictureSet copyContent = PictureSet(m_content); - done = m_progressDone; - m_contentMutex.unlock(); - DBG_NAV_LOGD("done=%s empty=%s", done ? "true" : "false", - copyContent.isEmpty() ? "true" : "false"); - return done || !copyContent.isEmpty(); -} - SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval) { WebCore::FrameView* view = m_mainFrame->view(); @@ -851,48 +804,52 @@ void WebViewCore::rebuildPictureSet(PictureSet* pictureSet) pictureSet->validate(__FUNCTION__); } -bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point) +BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point) { DBG_SET_LOG("start"); float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); - m_contentMutex.lock(); - PictureSet contentCopy(m_content); m_progressDone = progress <= 0.0f || progress >= 1.0f; - m_contentMutex.unlock(); - recordPictureSet(&contentCopy); - if (!m_progressDone && contentCopy.isEmpty()) { + recordPictureSet(&m_content); + if (!m_progressDone && m_content.isEmpty()) { DBG_SET_LOGD("empty (progress=%g)", progress); - return false; + return 0; } region->set(m_addInval); m_addInval.setEmpty(); region->op(m_rebuildInval, SkRegion::kUnion_Op); m_rebuildInval.setEmpty(); - m_contentMutex.lock(); - contentCopy.setDrawTimes(m_content); - m_content.set(contentCopy); point->fX = m_content.width(); point->fY = m_content.height(); - m_contentMutex.unlock(); DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft, region->getBounds().fTop, region->getBounds().fRight, region->getBounds().fBottom); DBG_SET_LOG("end"); - return true; + + BaseLayerAndroid* base = new BaseLayerAndroid(); + base->setContent(m_content); + +#if USE(ACCELERATED_COMPOSITING) + // We update the layers + ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client()); + GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync()); + if (root) { + root->notifyClientAnimationStarted(); + LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer()); + base->addChild(copyLayer); + copyLayer->unref(); + } +#endif + + return base; } -void WebViewCore::splitContent() +void WebViewCore::splitContent(PictureSet* content) { bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame); LOG_ASSERT(layoutSuceeded, "Can never be called recursively"); - PictureSet tempPictureSet; - m_contentMutex.lock(); - m_content.split(&tempPictureSet); - m_contentMutex.unlock(); - rebuildPictureSet(&tempPictureSet); - m_contentMutex.lock(); - m_content.set(tempPictureSet); - m_contentMutex.unlock(); + content->split(&m_content); + rebuildPictureSet(&m_content); + content->set(m_content); } void WebViewCore::scrollTo(int x, int y, bool animate) @@ -936,28 +893,6 @@ void WebViewCore::scrollBy(int dx, int dy, bool animate) checkException(env); } -#if USE(ACCELERATED_COMPOSITING) - -void WebViewCore::immediateRepaint() -{ - LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - m_javaGlue->m_sendImmediateRepaint); - checkException(env); -} - -void WebViewCore::setUIRootLayer(const LayerAndroid* layer) -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - m_javaGlue->m_setRootLayer, - reinterpret_cast<jint>(layer)); - checkException(env); -} - -#endif // USE(ACCELERATED_COMPOSITING) - void WebViewCore::contentDraw() { JNIEnv* env = JSC::Bindings::getJNIEnv(); @@ -1034,24 +969,13 @@ void WebViewCore::updateViewport() checkException(env); } -void WebViewCore::restoreScale(int scale) +void WebViewCore::restoreScale(int scale, int textWrapScale) { DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale); - checkException(env); -} - -void WebViewCore::restoreScreenWidthScale(int scale) -{ - DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); - LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); - - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - m_javaGlue->m_restoreScreenWidthScale, scale); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale); checkException(env); } @@ -1162,42 +1086,45 @@ void WebViewCore::setGlobalBounds(int x, int y, int h, int v) } void WebViewCore::setSizeScreenWidthAndScale(int width, int height, - int screenWidth, float scale, int realScreenWidth, int screenHeight, + int textWrapWidth, float scale, int screenWidth, int screenHeight, int anchorX, int anchorY, bool ignoreHeight) { WebCoreViewBridge* window = m_mainFrame->view()->platformWidget(); int ow = window->width(); int oh = window->height(); window->setSize(width, height); + window->setVisibleSize(screenWidth, screenHeight); + if (width != screenWidth) { + m_mainFrame->view()->setUseFixedLayout(true); + m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height)); + } else { + m_mainFrame->view()->setUseFixedLayout(false); + } int osw = m_screenWidth; - int orsw = m_screenWidth * m_screenWidthScale / m_scale; int osh = m_screenHeight; + int otw = m_textWrapWidth; DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)", ow, oh, osw, m_scale, width, height, screenWidth, scale); m_screenWidth = screenWidth; m_screenHeight = screenHeight; - if (scale >= 0) { // negative means ignore + m_textWrapWidth = textWrapWidth; + if (scale >= 0) // negative means keep the current scale m_scale = scale; - if (screenWidth != realScreenWidth) - m_screenWidthScale = realScreenWidth * scale / screenWidth; - else - m_screenWidthScale = m_scale; - } m_maxXScroll = screenWidth >> 2; - m_maxYScroll = (screenWidth * height / width) >> 2; - if (ow != width || (!ignoreHeight && oh != height) || osw != screenWidth) { + m_maxYScroll = m_maxXScroll * height / width; + if (ow != width || (!ignoreHeight && oh != height) || otw != textWrapWidth) { WebCore::RenderObject *r = m_mainFrame->contentRenderer(); DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r, - realScreenWidth, screenHeight); + screenWidth, screenHeight); if (r) { WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY); DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY); - WebCore::Node* node = 0; + RefPtr<WebCore::Node> node; WebCore::IntRect bounds; WebCore::IntPoint offset; - // If the screen width changed, it is probably zoom change or + // If the text wrap changed, it is probably zoom change or // orientation change. Try to keep the anchor at the same place. - if (osw && screenWidth && osw != screenWidth) { + if (otw && textWrapWidth && otw != textWrapWidth) { WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()-> hitTestResultAtPoint( anchorPoint, false); @@ -1227,19 +1154,19 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d," "h=%d)", newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height()); - if ((orsw && osh && bounds.width() && bounds.height()) + if ((osw && osh && bounds.width() && bounds.height()) && (bounds != newBounds)) { WebCore::FrameView* view = m_mainFrame->view(); // force left align if width is not changed while height changed. // the anchorPoint is probably at some white space in the node // which is affected by text wrap around the screen width. - const bool leftAlign = (osw != m_screenWidth) + const bool leftAlign = (otw != textWrapWidth) && (bounds.width() == newBounds.width()) && (bounds.height() != newBounds.height()); const float xPercentInDoc = leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width(); const float xPercentInView = - leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / orsw; + leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw; const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height(); const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh; showRect(newBounds.x(), newBounds.y(), newBounds.width(), @@ -1272,7 +1199,7 @@ void WebViewCore::dumpDomTree(bool useFile) void WebViewCore::dumpRenderTree(bool useFile) { #ifdef ANDROID_DOM_LOGGING - WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); + WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); const char* data = renderDump.data(); if (useFile) { gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w"); @@ -1331,7 +1258,7 @@ WebCore::String WebViewCore::requestLabel(WebCore::Frame* frame, for (unsigned i = 0; i < length; i++) { WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>( list->item(i)); - if (label->correspondingControl() == node) { + if (label->control() == node) { Node* node = label; String result; while ((node = node->traverseNextNode(label))) { @@ -1437,6 +1364,252 @@ void WebViewCore::updateFrameCacheIfLoading() updateFrameCache(); } +struct TouchNodeData { + Node* mNode; + IntRect mBounds; +}; + +// get the bounding box of the Node +static IntRect getAbsoluteBoundingBox(Node* node) { + IntRect rect; + RenderObject* render = node->renderer(); + if (render->isRenderInline()) + rect = toRenderInline(render)->linesVisibleOverflowBoundingBox(); + else if (render->isBox()) + rect = toRenderBox(render)->visualOverflowRect(); + else if (render->isText()) + rect = toRenderText(render)->linesBoundingBox(); + else + LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName()); + FloatPoint absPos = render->localToAbsolute(); + rect.move(absPos.x(), absPos.y()); + return rect; +} + +// get the highlight rectangles for the touch point (x, y) with the slop +Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop) +{ + Vector<IntRect> rects; + m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); +#ifdef ANDROID_HITTEST_WITHSIZE + HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), + false, false, DontHitTestScrollbars, IntSize(slop, slop)); + if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { + LOGE("Should not happen: no in document Node found"); + return rects; + } + const Vector<RefPtr<Node> >& list = hitTestResult.rawNodeList(); + if (list.isEmpty()) { + LOGE("Should not happen: no raw node found"); + return rects; + } + Frame* frame = hitTestResult.innerNode()->document()->frame(); + Vector<TouchNodeData> nodeDataList; + Vector<RefPtr<Node> >::const_iterator last = list.end(); + for (Vector<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) { + // TODO: it seems reasonable to not search across the frame. Isn't it? + // if the node is not in the same frame as the innerNode, skip it + if (it->get()->document()->frame() != frame) + continue; + // traverse up the tree to find the first node that needs highlight + bool found = false; + Node* eventNode = it->get(); + while (eventNode) { + RenderObject* render = eventNode->renderer(); + if (render->isBody() || render->isRenderView()) + break; + if (eventNode->supportsFocus() + || eventNode->hasEventListeners(eventNames().clickEvent) + || eventNode->hasEventListeners(eventNames().mousedownEvent) + || eventNode->hasEventListeners(eventNames().mouseupEvent)) { + found = true; + break; + } + // the nodes in the rawNodeList() are ordered based on z-index during hit testing. + // so do not search for the eventNode across explicit z-index border. + // TODO: this is a hard one to call. z-index is quite complicated as its value only + // matters when you compare two RenderLayer in the same hierarchy level. e.g. in + // the following example, "b" is on the top as its z level is the highest. even "c" + // has 100 as z-index, it is still below "d" as its parent has the same z-index as + // "d" and logically before "d". Of course "a" is the lowest in the z level. + // + // z-index:auto "a" + // z-index:2 "b" + // z-index:1 + // z-index:100 "c" + // z-index:1 "d" + // + // If the fat point touches everyone, the order in the list should be "b", "d", "c" + // and "a". When we search for the event node for "b", we really don't want "a" as + // in the z-order it is behind everything else. + if (!render->style()->hasAutoZIndex()) + break; + eventNode = eventNode->parentNode(); + } + // didn't find any eventNode, skip it + if (!found) + continue; + // first quick check whether it is a duplicated node before computing bounding box + Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); + for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { + // found the same node, skip it + if (eventNode == n->mNode) { + found = false; + break; + } + } + if (!found) + continue; + // next check whether the node is fully covered by or fully covering another node. + found = false; + IntRect rect = getAbsoluteBoundingBox(eventNode); + if (rect.isEmpty()) { + // if the node's bounds is empty and it is not a ContainerNode, skip it. + if (!eventNode->isContainerNode()) + continue; + // if the node's children are all positioned objects, its bounds can be empty. + // Walk through the children to find the bounding box. + Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild(); + while (child) { + IntRect childrect; + if (child->renderer()) + childrect = getAbsoluteBoundingBox(child); + if (!childrect.isEmpty()) { + rect.unite(childrect); + child = child->traverseNextSibling(eventNode); + } else + child = child->traverseNextNode(eventNode); + } + } + for (int i = nodeDataList.size() - 1; i >= 0; i--) { + TouchNodeData n = nodeDataList.at(i); + // the new node is enclosing an existing node, skip it + if (rect.contains(n.mBounds)) { + found = true; + break; + } + // the new node is fully inside an existing node, remove the existing node + if (n.mBounds.contains(rect)) + nodeDataList.remove(i); + } + if (!found) { + TouchNodeData newNode; + newNode.mNode = eventNode; + newNode.mBounds = rect; + nodeDataList.append(newNode); + } + } + if (!nodeDataList.size()) + return rects; + // finally select the node with the largest overlap with the fat point + TouchNodeData final; + final.mNode = 0; + IntPoint docPos = frame->view()->windowToContents(m_mousePos); + IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1); + int area = 0; + Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); + for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { + IntRect rect = n->mBounds; + rect.intersect(testRect); + int a = rect.width() * rect.height(); + if (a > area) { + final = *n; + area = a; + } + } + // now get the node's highlight rectangles in the page coordinate system + if (final.mNode) { + IntPoint frameAdjust; + if (frame != m_mainFrame) { + frameAdjust = frame->view()->contentsToWindow(IntPoint()); + frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY); + } + if (final.mNode->isLink()) { + // most of the links are inline instead of box style. So the bounding box is not + // a good representation for the highlights. Get the list of rectangles instead. + RenderObject* render = final.mNode->renderer(); + IntPoint offset = roundedIntPoint(render->localToAbsolute()); + render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y()); + bool inside = false; + int distance = INT_MAX; + int newx = x, newy = y; + int i = rects.size(); + while (i--) { + if (rects[i].isEmpty()) { + rects.remove(i); + continue; + } + // check whether the point (x, y) is inside one of the rectangles. + if (inside) + continue; + if (rects[i].contains(x, y)) { + inside = true; + continue; + } + if (x >= rects[i].x() && x < rects[i].right()) { + if (y < rects[i].y()) { + if (rects[i].y() - y < distance) { + newx = x; + newy = rects[i].y(); + distance = rects[i].y() - y; + } + } else if (y >= rects[i].bottom()) { + if (y - rects[i].bottom() + 1 < distance) { + newx = x; + newy = rects[i].bottom() - 1; + distance = y - rects[i].bottom() + 1; + } + } + } else if (y >= rects[i].y() && y < rects[i].bottom()) { + if (x < rects[i].x()) { + if (rects[i].x() - x < distance) { + newx = rects[i].x(); + newy = y; + distance = rects[i].x() - x; + } + } else if (x >= rects[i].right()) { + if (x - rects[i].right() + 1 < distance) { + newx = rects[i].right() - 1; + newy = y; + distance = x - rects[i].right() + 1; + } + } + } + } + if (!rects.isEmpty()) { + if (!inside) { + // if neither x nor y has overlap, just pick the top/left of the first rectangle + if (newx == x && newy == y) { + newx = rects[0].x(); + newy = rects[0].y(); + } + m_mousePos.setX(newx - m_scrollOffsetX); + m_mousePos.setY(newy - m_scrollOffsetY); + DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", + x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, + m_scrollOffsetX, m_scrollOffsetY); + } + return rects; + } + } + IntRect rect = final.mBounds; + rect.move(frameAdjust.x(), frameAdjust.y()); + rects.append(rect); + // adjust m_mousePos if it is not inside the returned highlight rectangle + testRect.move(frameAdjust.x(), frameAdjust.y()); + testRect.intersect(rect); + if (!testRect.contains(x, y)) { + m_mousePos = testRect.center(); + m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY); + DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", + x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, + m_scrollOffsetX, m_scrollOffsetY); + } + } +#endif + return rects; +} + /////////////////////////////////////////////////////////////////////////////// void WebViewCore::addPlugin(PluginWidgetAndroid* w) @@ -1675,6 +1848,51 @@ void WebViewCore::setSelection(int start, int end) setFocusControllerActive(focusedFrame, true); } +String WebViewCore::modifySelection(const String& alter, const String& direction, const String& granularity) +{ + DOMSelection* selection = m_mainFrame->domWindow()->getSelection(); + + if (selection->rangeCount() == 0) { + Document* document = m_mainFrame->document(); + HTMLElement* body = document->body(); + ExceptionCode ec; + + PassRefPtr<Range> rangeRef = document->createRange(); + rangeRef->setStart(PassRefPtr<Node>(body), 0, ec); + if (ec) { + LOGE("Error setting range start. Error code: %d", ec); + return String(); + } + + rangeRef->setEnd(PassRefPtr<Node>(body), 0, ec); + if (ec) { + LOGE("Error setting range end. Error code: %d", ec); + return String(); + } + + selection->addRange(rangeRef.get()); + } + + if (equalIgnoringCase(direction, "forward")) { + selection->collapseToEnd(); + } else if (equalIgnoringCase(direction, "backward")) { + selection->collapseToStart(); + } else { + LOGE("Invalid direction: %s", direction.utf8().data()); + return String(); + } + + // NOTE: The selection of WebKit misbehaves and I need to add some + // voodoo here to force it behave well. Rachel did something similar + // in JS and I want to make sure it is optimal before adding it here. + + selection->modify(alter, direction, granularity); + String selection_string = selection->toString(); + LOGD("Selection string: %s", selection_string.utf8().data()); + + return selection_string; +} + void WebViewCore::deleteSelection(int start, int end, int textGeneration) { setSelection(start, end); @@ -1918,15 +2136,24 @@ void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) { if (!chooser) return; JNIEnv* env = JSC::Bindings::getJNIEnv(); + + WebCore::String acceptType = chooser->acceptTypes(); + jstring jAcceptType = env->NewString(const_cast<unsigned short*>(acceptType.characters()), acceptType.length()); jstring jName = (jstring) env->CallObjectMethod( - m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser); + m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType); checkException(env); - const UChar* string = (const UChar*) env->GetStringChars(jName, NULL); + env->DeleteLocalRef(jAcceptType); + + const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL)); + if (!string) return; + WebCore::String webcoreString = to_string(env, jName); env->ReleaseStringChars(jName, string); - chooser->chooseFile(webcoreString); + + if (webcoreString.length()) + chooser->chooseFile(webcoreString); } void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, @@ -1985,12 +2212,25 @@ void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, s bool WebViewCore::key(const PlatformKeyboardEvent& event) { - WebCore::EventHandler* eventHandler = m_mainFrame->eventHandler(); + WebCore::EventHandler* eventHandler; WebCore::Node* focusNode = currentFocus(); - if (focusNode) - eventHandler = focusNode->document()->frame()->eventHandler(); DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p", event.keyIdentifier().utf8().data(), event.unichar(), focusNode); + if (focusNode) { + WebCore::Frame* frame = focusNode->document()->frame(); + eventHandler = frame->eventHandler(); + if (focusNode->isContentEditable()) { + // keyEvent will return true even if the contentEditable did not + // change its selection. In the case that it does not, we want to + // return false so that the key will be sent back to our navigation + // system. + VisibleSelection old = frame->selection()->selection(); + eventHandler->keyEvent(event); + return frame->selection()->selection() != old; + } + } else { + eventHandler = m_mainFrame->eventHandler(); + } return eventHandler->keyEvent(event); } @@ -2075,14 +2315,6 @@ bool WebViewCore::handleTouchEvent(int action, int x, int y, int metaState) // Track previous touch and if stationary set the state. WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY); -// handleTouchEvent() in EventHandler.cpp doesn't handle TouchStationary, which -// causes preventDefault be false when it returns. As our Java side may continue -// process the events if WebKit doesn't, it can cause unexpected result. -// if (type == WebCore::TouchMove && pt == m_lastTouchPoint) -// touchState = WebCore::PlatformTouchPoint::TouchStationary; - - m_lastTouchPoint = pt; - WebCore::PlatformTouchEvent te(pt, type, touchState, metaState); preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te); #endif @@ -2097,6 +2329,16 @@ bool WebViewCore::handleTouchEvent(int action, int x, int y, int metaState) void WebViewCore::touchUp(int touchGeneration, WebCore::Frame* frame, WebCore::Node* node, int x, int y) { + if (touchGeneration == 0) { + // m_mousePos should be set in getTouchHighlightRects() + WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false); + node = hitTestResult.innerNode(); + if (node) + frame = node->document()->frame(); + else + frame = 0; + DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame); + } else { if (m_touchGeneration > touchGeneration) { DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d" " x=%d y=%d", m_touchGeneration, touchGeneration, x, y); @@ -2106,6 +2348,7 @@ void WebViewCore::touchUp(int touchGeneration, // m_mousePos to determine where the click happens. moveMouse(frame, x, y); m_lastGeneration = touchGeneration; + } if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) { frame->loader()->resetMultipleFormSubmissionProtection(); } @@ -2203,6 +2446,9 @@ bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* node } else { requestKeyboard(false); } + } else if (focusNode->isContentEditable()) { + setFocusControllerActive(framePtr, true); + requestKeyboard(true); } } return handled; @@ -2542,6 +2788,13 @@ void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode checkException(env); } +void WebViewCore::notifyWebAppCanBeInstalled() +{ + JNIEnv* env = JSC::Bindings::getJNIEnv(); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp); + checkException(env); +} + //---------------------------------------------------------------------- // Native JNI methods //---------------------------------------------------------------------- @@ -2568,7 +2821,7 @@ static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj) } static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, - jint screenWidth, jfloat scale, jint realScreenWidth, jint screenHeight, + jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight, jint anchorX, jint anchorY, jboolean ignoreHeight) { #ifdef ANDROID_INSTRUMENT @@ -2577,8 +2830,8 @@ static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl); LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize"); - viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, scale, - realScreenWidth, screenHeight, anchorX, anchorY, ignoreHeight); + viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale, + screenWidth, screenHeight, anchorX, anchorY, ignoreHeight); } static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint x, jint y) @@ -2646,6 +2899,22 @@ static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end) viewImpl->setSelection(start, end); } +static jstring ModifySelection(JNIEnv *env, jobject obj, jstring alter, jstring direction, jstring granularity) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); +#endif + String alterString = to_string(env, alter); + String directionString = to_string(env, direction); + String granularityString = to_string(env, granularity); + + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + String selection_string = viewImpl->modifySelection(alterString, + directionString, + granularityString); + + return WebCoreStringToJString(env, selection_string); +} static void ReplaceTextfieldText(JNIEnv *env, jobject obj, jint oldStart, jint oldEnd, jstring replace, jint start, jint end, @@ -2710,7 +2979,7 @@ void WebViewCore::addVisitedLink(const UChar* string, int length) m_groupForVisitedLinks->addVisitedLink(string, length); } -static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) +static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); @@ -2718,18 +2987,18 @@ static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region); SkIPoint nativePt; - bool result = viewImpl->recordContent(nativeRegion, &nativePt); + BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt); GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt); - return result; + return reinterpret_cast<jint>(result); } -static void SplitContent(JNIEnv *env, jobject obj) +static void SplitContent(JNIEnv *env, jobject obj, jint content) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); #endif WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - viewImpl->splitContent(); + viewImpl->splitContent(reinterpret_cast<PictureSet*>(content)); } static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice) @@ -2981,7 +3250,7 @@ static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags) { #if USE(V8) WebCore::String flagsString = to_string(env, flags); - WebCore::CString utf8String = flagsString.utf8(); + WTF::CString utf8String = flagsString.utf8(); WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length()); #endif } @@ -3018,45 +3287,11 @@ static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) { WebCore::SecurityOrigin::registerURLSchemeAsLocal(to_string(env, scheme)); } -static void ClearContent(JNIEnv *env, jobject obj) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); -#endif - WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - viewImpl->clearContent(); -} - -static void CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); -#endif - WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - if (!viewImpl) - return; - SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); - viewImpl->copyContentToPicture(picture); -} - -static bool DrawContent(JNIEnv *env, jobject obj, jobject canv, jint color) -{ - // Note: this is called from UI thread, don't count it for WebViewCoreTimeCounter - WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); - return viewImpl->drawContent(canvas, color); -} - static bool FocusBoundsChanged(JNIEnv* env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged(); } -static bool PictureReady(JNIEnv* env, jobject obj) -{ - return GET_NATIVE_VIEW(env, obj)->pictureReady(); -} - static void Pause(JNIEnv* env, jobject obj) { // This is called for the foreground tab when the browser is put to the @@ -3151,26 +3386,54 @@ static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node, reinterpret_cast<Node*>(node), nativeRect); } +static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + if (!viewImpl) + return NULL; + Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop); + if (rects.isEmpty()) + return NULL; + + jclass arrayClass = env->FindClass("java/util/ArrayList"); + LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList"); + jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V"); + LOG_ASSERT(init, "Could not find constructor for ArrayList"); + jobject array = env->NewObject(arrayClass, init, rects.size()); + LOG_ASSERT(vector, "Could not create a new ArrayList"); + jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z"); + LOG_ASSERT(add, "Could not find add method on ArrayList"); + jclass rectClass = env->FindClass("android/graphics/Rect"); + LOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); + jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V"); + LOG_ASSERT(rectinit, "Could not find init method on Rect"); + + for (size_t i = 0; i < rects.size(); i++) { + jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(), + rects[i].y(), rects[i].right(), rects[i].bottom()); + if (rect) { + env->CallBooleanMethod(array, add, rect); + env->DeleteLocalRef(rect); + } + } + + env->DeleteLocalRef(rectClass); + env->DeleteLocalRef(arrayClass); + return array; +} + // ---------------------------------------------------------------------------- /* * JNI registration. */ static JNINativeMethod gJavaWebViewCoreMethods[] = { - { "nativeClearContent", "()V", - (void*) ClearContent }, - { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V", - (void*) CopyContentToPicture }, - { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z", - (void*) DrawContent } , { "nativeFocusBoundsChanged", "()Z", (void*) FocusBoundsChanged } , { "nativeKey", "(IIIZZZZ)Z", (void*) Key }, { "nativeClick", "(II)V", (void*) Click }, - { "nativePictureReady", "()Z", - (void*) PictureReady } , { "nativeSendListBoxChoices", "([ZI)V", (void*) SendListBoxChoices }, { "nativeSendListBoxChoice", "(I)V", @@ -3183,6 +3446,8 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) SetGlobalBounds }, { "nativeSetSelection", "(II)V", (void*) SetSelection } , + { "nativeModifySelection", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) ModifySelection }, { "nativeDeleteSelection", "(III)V", (void*) DeleteSelection } , { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V", @@ -3215,11 +3480,11 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) UpdateFrameCache }, { "nativeGetContentMinPrefWidth", "()I", (void*) GetContentMinPrefWidth }, - { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z", + { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I", (void*) RecordContent }, { "setViewportSettingsFromNative", "()V", (void*) SetViewportSettingsFromNative }, - { "nativeSplitContent", "()V", + { "nativeSplitContent", "(I)V", (void*) SplitContent }, { "nativeSetBackgroundColor", "(I)V", (void*) SetBackgroundColor }, @@ -3251,6 +3516,8 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) FullScreenPluginHidden }, { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z", (void*) ValidNodeAndBounds }, + { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;", + (void*) GetTouchHighlightRects }, }; int register_webviewcore(JNIEnv* env) diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h index 056dba1..99f02e9 100644 --- a/WebKit/android/jni/WebViewCore.h +++ b/WebKit/android/jni/WebViewCore.h @@ -61,10 +61,13 @@ namespace WebCore { #if USE(ACCELERATED_COMPOSITING) namespace WebCore { class GraphicsLayerAndroid; - class LayerAndroid; } #endif +namespace WebCore { + class BaseLayerAndroid; +} + struct PluginWidgetAndroid; class SkPicture; class SkIRect; @@ -137,8 +140,6 @@ namespace android { #if USE(ACCELERATED_COMPOSITING) GraphicsLayerAndroid* graphicsRootLayer() const; - void immediateRepaint(); - void setUIRootLayer(const LayerAndroid* layer); #endif /** Invalidate the view/screen, NOT the content/DOM, but expressed in @@ -170,15 +171,9 @@ namespace android { /** * Notify the view to restore the screen width, which in turn restores - * the scale. - */ - void restoreScale(int); - - /** - * Notify the view to restore the scale used to calculate the screen - * width for wrapping the text + * the scale. Also restore the scale for the text wrap. */ - void restoreScreenWidthScale(int); + void restoreScale(int scale, int textWrapScale); /** * Tell the java side to update the focused textfield @@ -325,6 +320,18 @@ namespace android { * If start and end are out of order, swap them. */ void setSelection(int start, int end); + + /** + * Modifies the current selection. + * + * alter - Specifies how to alter the selection. + * direction - The direction in which to alter the selection. + * granularity - The granularity of the selection modification. + * + * returns - The selection as string. + */ + String modifySelection(const String& alter, const String& direction, const String& granularity); + /** * In the currently focused textfield, replace the characters from oldStart to oldEnd * (if oldStart == oldEnd, this will be an insert at that position) with replace, @@ -432,6 +439,9 @@ namespace android { // in the current view. void centerFitRect(int x, int y, int width, int height); + // return a list of rects matching the touch point (x, y) with the slop + Vector<IntRect> getTouchHighlightRects(int x, int y, int slop); + // other public functions public: // Open a file chooser for selecting a file to upload @@ -440,27 +450,22 @@ namespace android { // reset the picture set to empty void clearContent(); - // flatten the picture set to a picture - void copyContentToPicture(SkPicture* ); - - // draw the picture set with the specified background color - bool drawContent(SkCanvas* , SkColor ); bool focusBoundsChanged(); - bool pictureReady(); // record the inval area, and the picture size - bool recordContent(SkRegion* , SkIPoint* ); - int screenWidth() const { return m_screenWidth; } - int screenHeight() const { return m_screenHeight; } + BaseLayerAndroid* recordContent(SkRegion* , SkIPoint* ); + int textWrapWidth() const { return m_textWrapWidth; } float scale() const { return m_scale; } - float screenWidthScale() const { return m_screenWidthScale; } + float textWrapScale() const { return m_screenWidth * m_scale / m_textWrapWidth; } WebCore::Frame* mainFrame() const { return m_mainFrame; } void updateCursorBounds(const CachedRoot* root, const CachedFrame* cachedFrame, const CachedNode* cachedNode); void updateFrameCacheIfLoading(); // utility to split slow parts of the picture set - void splitContent(); + void splitContent(PictureSet*); + + void notifyWebAppCanBeInstalled(); // these members are shared with webview.cpp static Mutex gFrameCacheMutex; @@ -510,8 +515,7 @@ namespace android { WebCore::IntRect m_lastFocusedBounds; int m_lastFocusedSelStart; int m_lastFocusedSelEnd; - static Mutex m_contentMutex; // protects ui/core thread pictureset access - PictureSet m_content; // the set of pictures to draw (accessed by UI too) + 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 // Used in passToJS to avoid updating the UI text field until after the @@ -536,8 +540,8 @@ 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 + int m_textWrapWidth; float m_scale; - float m_screenWidthScale; unsigned m_domtree_version; bool m_check_domtree_version; PageGroup* m_groupForVisitedLinks; @@ -558,7 +562,6 @@ namespace android { #if ENABLE(TOUCH_EVENTS) bool m_forwardingTouchEvents; - IntPoint m_lastTouchPoint; #endif #if DEBUG_NAV_UI diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp index 7ee2a16..1c8af5e 100644 --- a/WebKit/android/nav/CacheBuilder.cpp +++ b/WebKit/android/nav/CacheBuilder.cpp @@ -412,7 +412,7 @@ void CacheBuilder::Debug::groups() { comma(scratch); Element* element = static_cast<Element*>(node); if (node->isElementNode() && element->hasID()) - wideString(element->getIDAttribute()); + wideString(element->getIdAttribute()); else if (node->isTextNode()) { #if 01 // set to one to abbreviate text that can be omitted from the address detection code if (rect.isEmpty() && node->textContent().length() > 100) { @@ -469,22 +469,48 @@ void CacheBuilder::Debug::groups() { } } } - count++; - newLine(); -#if USE(ACCELERATED_COMPOSITING) - if (renderer && layer) { + if (renderer) { + RenderStyle* style = renderer->style(); + snprintf(scratch, sizeof(scratch), "// renderStyle:" + " visibility=%s hasBackGround=%d" + " tapHighlightColor().alpha()=0x%02x", + style->visibility() == HIDDEN ? "HIDDEN" : "VISIBLE", + renderer->hasBackground(), style->tapHighlightColor().alpha()); + newLine(); + print(scratch); + RenderBlock* renderBlock = static_cast<RenderBlock*>(renderer); + if (renderer->isRenderBlock() && renderBlock->hasColumns()) { + const RenderBox* box = static_cast<RenderBox*>(renderer); + const IntRect& oRect = box->visibleOverflowRect(); + snprintf(scratch, sizeof(scratch), "// renderBlock:" + " columnRects=%d columnGap=%d direction=%d" + " hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)", + renderBlock->columnRects(), renderBlock->columnGap(), + renderBlock->style()->direction(), renderer->hasOverflowClip(), + oRect.x(), oRect.y(), oRect.width(), oRect.height()); + newLine(); + print(scratch); + } + } + #if USE(ACCELERATED_COMPOSITING) + if (renderer && renderer->hasLayer()) { + RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); RenderLayerBacking* back = layer->backing(); GraphicsLayerAndroid* grLayer = static_cast <GraphicsLayerAndroid*>(back ? back->graphicsLayer() : 0); LayerAndroid* aLayer = grLayer ? grLayer->contentLayer() : 0; const SkPicture* pict = aLayer ? aLayer->picture() : 0; + const IntRect& r = renderer->absoluteBoundingBoxRect(); snprintf(scratch, sizeof(scratch), "// layer:%p back:%p" - " gLayer:%p aLayer:%p pict:%p", layer, back, grLayer, - aLayer, pict); - print(scratch); + " gLayer:%p aLayer:%p pict:%p r:(%d,%d,w=%d,h=%d)", + layer, back, grLayer, aLayer, pict, r.x(), r.y(), + r.width(), r.height()); newLine(); - } -#endif + print(scratch); + } + #endif + count++; + newLine(); } while ((node = node->traverseNextNode()) != NULL); DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1); DUMP_NAV_LOGD("\n"); @@ -841,6 +867,7 @@ bool CacheBuilder::AnyIsClick(Node* node) void CacheBuilder::buildCache(CachedRoot* root) { Frame* frame = FrameAnd(this); + mPictureSetDisabled = false; BuildFrame(frame, frame, root, (CachedFrame*) root); root->finishInit(); // set up frame parent pointers, child pointers setData((CachedFrame*) root); @@ -1028,7 +1055,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, RenderStyle* style = nodeRenderer->style(); if (style->visibility() == HIDDEN) continue; - isTransparent = style->hasBackground() == false; + isTransparent = nodeRenderer->hasBackground() == false; #ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR hasCursorRing = style->tapHighlightColor().alpha() > 0; #endif @@ -1105,6 +1132,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, if (node->hasTagName(HTMLNames::bodyTag)) bodyPos = originalAbsBounds.location(); + else if (node->hasTagName(HTMLNames::canvasTag)) + mPictureSetDisabled = true; if (checkForPluginViewThatWantsFocus(nodeRenderer)) { bounds = absBounds; isUnclipped = true; @@ -1112,13 +1141,18 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, type = PLUGIN_CACHEDNODETYPE; goto keepNode; } + // Only use the root contentEditable element + if (node->isContentEditable() && !node->parent()->isContentEditable()) { + bounds = absBounds; + takesFocus = true; + type = CONTENT_EDITABLE_CACHEDNODETYPE; + goto keepNode; + } if (nodeRenderer->isRenderBlock()) { RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; if (renderBlock->hasColumns()) { columns = renderBlock->columnRects(); -#ifdef ANDROID_EXPOSE_COLUMN_GAP columnGap = renderBlock->columnGap(); -#endif direction = renderBlock->style()->direction(); } } @@ -1216,6 +1250,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string())) // Set the exported string for all non-javascript anchors. exported = href.string().threadsafeCopy(); + } else if (node->hasTagName(HTMLNames::selectTag)) { + type = SELECT_CACHEDNODETYPE; } if (type == TEXT_INPUT_CACHEDNODETYPE) { RenderTextControl* renderText = @@ -2673,7 +2709,8 @@ bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, do { do { do { - node = node->traverseNextNode(); + if (node) + node = node->traverseNextNode(); if (node == NULL || node->hasTagName(HTMLNames::aTag) || node->hasTagName(HTMLNames::inputTag) || node->hasTagName(HTMLNames::textareaTag)) { diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h index 4ded58d..8183954 100644 --- a/WebKit/android/nav/CacheBuilder.h +++ b/WebKit/android/nav/CacheBuilder.h @@ -94,6 +94,7 @@ public: static IntRect getAreaRect(const HTMLAreaElement* area); static void GetGlobalOffset(Frame* , int* x, int * y); static void GetGlobalOffset(Node* , int* x, int * y); + bool pictureSetDisabled() { return mPictureSetDisabled; } static bool validNode(Frame* startFrame, void* framePtr, void* nodePtr); private: enum AddressProgress { @@ -249,6 +250,7 @@ private: Node* tryFocus(Direction direction); Node* trySegment(Direction direction, int mainStart, int mainEnd); CachedNodeBits mAllowableTypes; + bool mPictureSetDisabled; #if DUMP_NAV_CACHE public: class Debug { diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp index ce5600b..ff13508 100644 --- a/WebKit/android/nav/CachedFrame.cpp +++ b/WebKit/android/nav/CachedFrame.cpp @@ -1390,6 +1390,7 @@ void CachedFrame::Debug::print() const const CachedInput* input = b->textInput(node); if (input) input->mDebug.print(); + DUMP_NAV_LOGD("\n"); } DUMP_NAV_LOGD("// }; // end of nodes\n"); #if USE(ACCELERATED_COMPOSITING) diff --git a/WebKit/android/nav/CachedInput.cpp b/WebKit/android/nav/CachedInput.cpp index 924bbca..608c41b 100644 --- a/WebKit/android/nav/CachedInput.cpp +++ b/WebKit/android/nav/CachedInput.cpp @@ -28,6 +28,11 @@ namespace android { +void CachedInput::init() { + bzero(this, sizeof(CachedInput)); + mName = WebCore::String(); +} + #if DUMP_NAV_CACHE #define DEBUG_PRINT_BOOL(field) \ @@ -55,7 +60,7 @@ void CachedInput::Debug::print() const { CachedInput* b = base(); printWebCoreString("// char* mName=\"", b->mName); - DUMP_NAV_LOGD("// void* mForm=%p;", b->mForm); + DUMP_NAV_LOGD("// void* mForm=%p;\n", b->mForm); DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength); DUMP_NAV_LOGD("// int mTextSize=%d;\n", b->mTextSize); DUMP_NAV_LOGD("// int mInputType=%d;\n", b->mInputType); diff --git a/WebKit/android/nav/CachedInput.h b/WebKit/android/nav/CachedInput.h index 42cadf1..a3d6b10 100644 --- a/WebKit/android/nav/CachedInput.h +++ b/WebKit/android/nav/CachedInput.h @@ -39,10 +39,7 @@ public: // constructor } void* formPointer() const { return mForm; } - void init() { - bzero(this, sizeof(CachedInput)); - mName = WebCore::String(); - } + void init(); WebCore::HTMLInputElement::InputType inputType() const { return mInputType; } bool isRtlText() const { return mIsRtlText; } bool isTextField() const { return mIsTextField; } diff --git a/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp index 0c9d541..8fc5f5b 100644 --- a/WebKit/android/nav/CachedNode.cpp +++ b/WebKit/android/nav/CachedNode.cpp @@ -367,6 +367,8 @@ const char* CachedNode::Debug::type(android::CachedNodeType t) const case FRAME_CACHEDNODETYPE: return "FRAME"; break; case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break; case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break; + case SELECT_CACHEDNODETYPE: return "SELECT"; break; + case CONTENT_EDITABLE_CACHEDNODETYPE: return "CONTENT_EDITABLE"; break; default: return "???"; } } @@ -419,7 +421,6 @@ void CachedNode::Debug::print() const DEBUG_PRINT_BOOL(mLast); DEBUG_PRINT_BOOL(mUseBounds); DEBUG_PRINT_BOOL(mUseHitBounds); - DUMP_NAV_LOGD("\n"); } #endif diff --git a/WebKit/android/nav/CachedNode.h b/WebKit/android/nav/CachedNode.h index f3cfd98..09f53c3 100644 --- a/WebKit/android/nav/CachedNode.h +++ b/WebKit/android/nav/CachedNode.h @@ -108,6 +108,7 @@ public: int index() const { return mIndex; } void init(WebCore::Node* node); bool isAnchor() const { return mType == ANCHOR_CACHEDNODETYPE; } + bool isContentEditable() const { return mType == CONTENT_EDITABLE_CACHEDNODETYPE; } bool isCursor() const { return mIsCursor; } bool isArea() const { return mType == AREA_CACHEDNODETYPE; } bool isFocus() const { return mIsFocus; } @@ -118,6 +119,7 @@ public: return clip.intersects(bounds(frame)); } bool isPlugin() const { return mType == PLUGIN_CACHEDNODETYPE; } + bool isSelect() const { return mType == SELECT_CACHEDNODETYPE; } bool isSyntheticLink() const { return mType >= ADDRESS_CACHEDNODETYPE && mType <= PHONE_CACHEDNODETYPE; } @@ -173,7 +175,7 @@ public: const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; } bool useBounds() const { return mUseBounds; } bool useHitBounds() const { return mUseHitBounds; } - bool wantsKeyEvents() const { return isTextInput() || isPlugin(); } + bool wantsKeyEvents() const { return isTextInput() || isPlugin() || isContentEditable(); } private: friend class CacheBuilder; WebCore::String mExport; diff --git a/WebKit/android/nav/CachedNodeType.h b/WebKit/android/nav/CachedNodeType.h index 21e2d40..8bc9328 100644 --- a/WebKit/android/nav/CachedNodeType.h +++ b/WebKit/android/nav/CachedNodeType.h @@ -37,7 +37,9 @@ enum CachedNodeType { AREA_CACHEDNODETYPE, FRAME_CACHEDNODETYPE, PLUGIN_CACHEDNODETYPE, - TEXT_INPUT_CACHEDNODETYPE + TEXT_INPUT_CACHEDNODETYPE, + SELECT_CACHEDNODETYPE, + CONTENT_EDITABLE_CACHEDNODETYPE }; enum CachedNodeBits { diff --git a/WebKit/android/nav/CachedPrefix.h b/WebKit/android/nav/CachedPrefix.h index b682288..73a5c2c 100644 --- a/WebKit/android/nav/CachedPrefix.h +++ b/WebKit/android/nav/CachedPrefix.h @@ -43,4 +43,11 @@ #define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning +#ifndef BZERO_DEFINED +#define BZERO_DEFINED +// http://www.opengroup.org/onlinepubs/000095399/functions/bzero.html +// For maximum portability, it is recommended to replace the function call to bzero() as follows: +#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) +#endif + #endif diff --git a/WebKit/android/nav/FindCanvas.cpp b/WebKit/android/nav/FindCanvas.cpp index d8e908b..8eaaaef 100644 --- a/WebKit/android/nav/FindCanvas.cpp +++ b/WebKit/android/nav/FindCanvas.cpp @@ -98,7 +98,7 @@ GlyphSet::~GlyphSet() { // part of mLowerGlyphs } -GlyphSet::GlyphSet& GlyphSet::operator=(GlyphSet& src) { +GlyphSet& GlyphSet::operator=(GlyphSet& src) { mTypeface = src.mTypeface; mCount = src.mCount; if (mCount > MAX_STORAGE_COUNT) { @@ -675,4 +675,3 @@ void FindOnPage::setMatches(WTF::Vector<MatchInfo>* matches) } } - diff --git a/WebKit/android/nav/FindCanvas.h b/WebKit/android/nav/FindCanvas.h index b9dbeea..34929ec 100644 --- a/WebKit/android/nav/FindCanvas.h +++ b/WebKit/android/nav/FindCanvas.h @@ -220,6 +220,7 @@ public: virtual ~FindOnPage() { delete m_matches; } void clearCurrentLocation() { m_hasCurrentLocation = false; } IntRect currentMatchBounds() const; + int currentMatchIndex() const { return m_findIndex; } bool currentMatchIsInLayer() const; virtual void draw(SkCanvas* , LayerAndroid* ); void findNext(bool forward); diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp index e471307..9df6ef5 100644 --- a/WebKit/android/nav/SelectText.cpp +++ b/WebKit/android/nav/SelectText.cpp @@ -23,9 +23,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define LOG_TAG "webcoreglue" +#define LOG_TAG "webviewglue" #include "CachedPrefix.h" +#include "BidiResolver.h" #include "CachedRoot.h" #include "LayerAndroid.h" #include "SelectText.h" @@ -39,26 +40,156 @@ #include "SkRect.h" #include "SkRegion.h" #include "SkUtils.h" +#include "TextRun.h" #ifdef DEBUG_NAV_UI -#include "CString.h" +#include <wtf/text/CString.h> #endif +#define VERBOSE_LOGGING 0 +// #define EXTRA_NOISY_LOGGING 1 + +// TextRunIterator has been copied verbatim from GraphicsContext.cpp +namespace WebCore { + +class TextRunIterator { +public: + TextRunIterator() + : m_textRun(0) + , m_offset(0) + { + } + + TextRunIterator(const TextRun* textRun, unsigned offset) + : m_textRun(textRun) + , m_offset(offset) + { + } + + TextRunIterator(const TextRunIterator& other) + : m_textRun(other.m_textRun) + , m_offset(other.m_offset) + { + } + + unsigned offset() const { return m_offset; } + void increment() { m_offset++; } + bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } + UChar current() const { return (*m_textRun)[m_offset]; } + WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); } + + bool operator==(const TextRunIterator& other) + { + return m_offset == other.m_offset && m_textRun == other.m_textRun; + } + + bool operator!=(const TextRunIterator& other) { return !operator==(other); } + +private: + const TextRun* m_textRun; + int m_offset; +}; + +// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText() +void ReverseBidi(UChar* chars, int len) { + using namespace WTF::Unicode; + WTF::Vector<UChar> result; + result.reserveCapacity(len); + TextRun run(chars, len); + BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; + bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight, + BidiContext::create(0, LeftToRight, false))); + bidiResolver.setPosition(TextRunIterator(&run, 0)); + bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len)); + if (!bidiResolver.runCount()) + return; + BidiCharacterRun* bidiRun = bidiResolver.firstRun(); + while (bidiRun) { + int bidiStart = bidiRun->start(); + int bidiStop = bidiRun->stop(); + int size = result.size(); + int bidiCount = bidiStop - bidiStart; + result.append(chars + bidiStart, bidiCount); + if (bidiRun->level() % 2) { + UChar* start = &result[size]; + UChar* end = start + bidiCount; + // reverse the order of any RTL substrings + while (start < end) { + UChar temp = *start; + *start++ = *--end; + *end = temp; + } + start = &result[size]; + end = start + bidiCount - 1; + // if the RTL substring had a surrogate pair, restore its order + while (start < end) { + UChar trail = *start++; + if (!U16_IS_SURROGATE(trail)) + continue; + start[-1] = *start; // lead + *start++ = trail; + } + } + bidiRun = bidiRun->next(); + } + bidiResolver.deleteRuns(); + memcpy(chars, &result[0], len * sizeof(UChar)); +} + +} + namespace android { +/* SpaceBounds and SpaceCanvas are used to measure the left and right side + * bearings of two consecutive glyphs to help determine if the glyphs were + * originally laid out with a space character between the glyphs. + */ +class SpaceBounds : public SkBounder { +public: + virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& rec) + { + mFirstGlyph = mLastGlyph; + mLastGlyph = rec; + return false; + } + + SkBounder::GlyphRec mFirstGlyph; + SkBounder::GlyphRec mLastGlyph; +}; + +class SpaceCanvas : public SkCanvas { +public: + SpaceCanvas(const SkIRect& area) + { + setBounder(&mBounder); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(), + area.height()); + setBitmapDevice(bitmap); + translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop)); + } + + SpaceBounds mBounder; +}; + +#define HYPHEN_MINUS 0x2D // ASCII hyphen +#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes +#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes +#define TOUCH_SLOP 10 // additional distance from character rect when hit + class CommonCheck : public SkBounder { public: - CommonCheck() : mMatrix(NULL), mPaint(NULL) {} - - virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y, - const void* text) { - mMatrix = &matrix; - mPaint = &paint; - mText = static_cast<const uint16_t*>(text); - mY = y; - mBase = mBottom = mTop = INT_MAX; + CommonCheck(int width, int height) + : mHeight(height) + , mLastUni(0) + , mMatrix(0) + , mPaint(0) + , mWidth(width) + { + mLastGlyph.fGlyphID = static_cast<uint16_t>(-1); + reset(); } - + int base() { if (mBase == INT_MAX) { SkPoint result; @@ -78,7 +209,148 @@ public: } return mBottom; } - + +#if DEBUG_NAV_UI + // make current (possibily uncomputed) value visible for debugging + int bottomDebug() const + { + return mBottom; + } +#endif + + bool addNewLine(const SkBounder::GlyphRec& rec) + { + SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY); + SkFixed lineHeight = SkIntToFixed(bottom() - top()); + return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5 + } + + bool addSpace(const SkBounder::GlyphRec& rec) + { + bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY; + if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR) + || mLastUni == HYPHEN_MINUS) && newBaseLine) + { + return false; + } + return isSpace(rec); + } + + void finishGlyph() + { + mLastGlyph = mLastCandidate; + mLastUni = mLastUniCandidate; + } + + SkUnichar getUniChar(const SkBounder::GlyphRec& rec) + { + SkUnichar unichar; + SkPaint utfPaint = *mPaint; + utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar); + return unichar; + } + + bool isSpace(const SkBounder::GlyphRec& rec) + { + DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)" + " rec=((%g, %g),(%g, %g), %d)" + " mMinSpaceWidth=%g mLastUni=0x%04x '%c'", + SkFixedToScalar(mLastGlyph.fLSB.fX), + SkFixedToScalar(mLastGlyph.fLSB.fY), + SkFixedToScalar(mLastGlyph.fRSB.fX), + SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID, + SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY), + SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY), + rec.fGlyphID, + SkFixedToScalar(mMinSpaceWidth), + mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?'); + bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY; + if (newBaseLine) + return true; + SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX; + SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX; + if (gapOne < 0 && gapTwo < 0) + return false; // overlaps + uint16_t test[2]; + test[0] = mLastGlyph.fGlyphID; + test[1] = rec.fGlyphID; + SkIRect area; + area.set(0, 0, mWidth, mHeight); + SpaceCanvas spaceChecker(area); + spaceChecker.drawText(test, sizeof(test), + SkFixedToScalar(mLastGlyph.fLSB.fX), + SkFixedToScalar(mLastGlyph.fLSB.fY), *mPaint); + const SkBounder::GlyphRec& g1 = spaceChecker.mBounder.mFirstGlyph; + const SkBounder::GlyphRec& g2 = spaceChecker.mBounder.mLastGlyph; + DBG_NAV_LOGD("g1=(%g, %g,%g, %g) g2=(%g, %g, %g, %g)", + SkFixedToScalar(g1.fLSB.fX), SkFixedToScalar(g1.fLSB.fY), + SkFixedToScalar(g1.fRSB.fX), SkFixedToScalar(g1.fRSB.fY), + SkFixedToScalar(g2.fLSB.fX), SkFixedToScalar(g2.fLSB.fY), + SkFixedToScalar(g2.fRSB.fX), SkFixedToScalar(g2.fRSB.fY)); + gapOne = SkFixedAbs(gapOne); + gapTwo = SkFixedAbs(gapTwo); + SkFixed gap = gapOne < gapTwo ? gapOne : gapTwo; + SkFixed overlap = g2.fLSB.fX - g1.fRSB.fX; + if (overlap < 0) + gap -= overlap; + DBG_NAV_LOGD("gap=%g overlap=%g gapOne=%g gapTwo=%g minSpaceWidth()=%g", + SkFixedToScalar(gap), SkFixedToScalar(overlap), + SkFixedToScalar(gapOne), SkFixedToScalar(gapTwo), + SkFixedToScalar(minSpaceWidth())); + // FIXME: the -1/2 below takes care of slop beween the computed gap + // and the actual space width -- it's a rounding error from + // moving from fixed to float and back and could be much smaller. + return gap >= minSpaceWidth() - SK_Fixed1 / 2; + } + + SkFixed minSpaceWidth() + { + if (mMinSpaceWidth == SK_FixedMax) { + SkPaint charPaint = *mPaint; + charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding); + SkScalar width = charPaint.measureText(" ", 1); + mMinSpaceWidth = SkScalarToFixed(width * mMatrix->getScaleX()); + DBG_NAV_LOGD("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)" + " mMinSpaceWidth=%g", width, + mMatrix->getScaleX(), mMatrix->getScaleY(), + mMatrix->getTranslateX(), mMatrix->getTranslateY(), + SkFixedToScalar(mMinSpaceWidth)); + } + return mMinSpaceWidth; + } + + void recordGlyph(const SkBounder::GlyphRec& rec) + { + mLastCandidate = rec; + mLastUniCandidate = getUniChar(rec); + } + + void reset() + { + mMinSpaceWidth = SK_FixedMax; // mark as uninitialized + mBase = mBottom = mTop = INT_MAX; // mark as uninitialized + } + + void set(CommonCheck& check) + { + mLastGlyph = check.mLastGlyph; + mLastUni = check.mLastUni; + mMatrix = check.mMatrix; + mPaint = check.mPaint; + reset(); + } + + void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y, + const void* text) + { + mMatrix = &matrix; + mPaint = &paint; + mText = static_cast<const uint16_t*>(text); + mY = y; + reset(); + } + int top() { if (mTop == INT_MAX) { SkPoint result; @@ -89,127 +361,417 @@ public: } return mTop; } - -protected: + +#if DEBUG_NAV_UI + // make current (possibily uncomputed) value visible for debugging + int topDebug() const + { + return mTop; + } +#endif + +protected: + int mHeight; + SkBounder::GlyphRec mLastCandidate; + SkBounder::GlyphRec mLastGlyph; + SkUnichar mLastUni; + SkUnichar mLastUniCandidate; const SkMatrix* mMatrix; const SkPaint* mPaint; const uint16_t* mText; + int mWidth; SkScalar mY; +private: int mBase; int mBottom; + SkFixed mMinSpaceWidth; int mTop; + friend class EdgeCheck; }; class FirstCheck : public CommonCheck { public: - FirstCheck(int x, int y) - : mDistance(INT_MAX), mFocusX(x), mFocusY(y) { - mBestBounds.setEmpty(); + FirstCheck(int x, int y, const SkIRect& area) + : INHERITED(area.width(), area.height()) + , mFocusX(x - area.fLeft) + , mFocusY(y - area.fTop) + , mRecordGlyph(false) + { + reset(); } - const SkIRect& bestBounds() { - DBG_NAV_LOGD("mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d", + const SkIRect& adjustedBounds(const SkIRect& area, int* base) + { + *base = mBestBase + area.fTop; + mBestBounds.offset(area.fLeft, area.fTop); + DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d", mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight, - mBestBounds.fBottom, mTop, mBottom); - return mBestBounds; - } - - void offsetBounds(int dx, int dy) { - mBestBounds.offset(dx, dy); + mBestBounds.fBottom, topDebug(), bottomDebug()); + return mBestBounds; } - - virtual bool onIRect(const SkIRect& rect) { - int dx = ((rect.fLeft + rect.fRight) >> 1) - mFocusX; - int dy = ((top() + bottom()) >> 1) - mFocusY; + + virtual bool onIRectGlyph(const SkIRect& rect, + const SkBounder::GlyphRec& rec) + { + /* compute distance from rectangle center. + * centerX = (rect.L + rect.R) / 2 + * multiply centerX and comparison x by 2 to retain better precision + */ + int dx = rect.fLeft + rect.fRight - (mFocusX << 1); + int dy = top() + bottom() - (mFocusY << 1); int distance = dx * dx + dy * dy; #ifdef EXTRA_NOISY_LOGGING if (distance < 500 || abs(distance - mDistance) < 500) - DBG_NAV_LOGD("distance=%d mDistance=%d", distance, mDistance); + DBG_NAV_LOGD("FirstCheck distance=%d mDistance=%d", distance, mDistance); #endif if (mDistance > distance) { - mDistance = distance; + mBestBase = base(); mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); -#ifdef EXTRA_NOISY_LOGGING - DBG_NAV_LOGD("mBestBounds={%d,%d,r=%d,b=%d}", - mBestBounds.fLeft, mBestBounds.fTop, - mBestBounds.fRight, mBestBounds.fBottom); -#endif + if (distance < 100) { + DBG_NAV_LOGD("FirstCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d", + mBestBounds.fLeft, mBestBounds.fTop, + mBestBounds.fRight, mBestBounds.fBottom, distance >> 2); + } + mDistance = distance; + if (mRecordGlyph) + recordGlyph(rec); } return false; } + + void reset() + { + mBestBounds.setEmpty(); + mDistance = INT_MAX; + } + + void setRecordGlyph() + { + mRecordGlyph = true; + } + protected: + int mBestBase; SkIRect mBestBounds; int mDistance; int mFocusX; int mFocusY; + bool mRecordGlyph; +private: + typedef CommonCheck INHERITED; +}; + +class EdgeCheck : public FirstCheck { +public: + EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left) + : INHERITED(x, y, area) + , mLast(area.width(), area.height()) + , mLeft(left) + { + mLast.set(last); + mLastGlyph = last.mLastGlyph; + mLastUni = last.mLastUni; + } + + bool adjacent() + { + return !mLast.isSpace(mLastGlyph); + } + + const SkIRect& bestBounds(int* base) + { + *base = mBestBase; + return mBestBounds; + } + + virtual bool onIRectGlyph(const SkIRect& rect, + const SkBounder::GlyphRec& rec) + { + int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX; + int dy = ((top() + bottom()) >> 1) - mFocusY; + if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) { + if (abs(dx) <= 10 && abs(dy) <= 10) { + DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d", + rect.fLeft, rect.fRight, mFocusX, dx, dy); + } + return false; + } + int distance = dx * dx + dy * dy; + if (mDistance > distance) { + if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) { + DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g", + SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX)); + return false; + } + recordGlyph(rec); + mDistance = distance; + mBestBase = base(); + mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); + if (distance <= 100) { + DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d", + mBestBounds.fLeft, mBestBounds.fTop, + mBestBounds.fRight, mBestBounds.fBottom, distance); + } + } + return false; + } + + void shiftStart(SkIRect bounds) + { + DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d", + mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight); + reset(); + mFocusX = mLeft ? bounds.fLeft : bounds.fRight; + mLast.set(*this); + } + +protected: + CommonCheck mLast; + bool mLeft; +private: + typedef FirstCheck INHERITED; +}; + +class FindFirst : public CommonCheck { +public: + FindFirst(int width, int height) + : INHERITED(width, height) + { + mBestBounds.set(width, height, width, height); + } + + const SkIRect& bestBounds(int* base) + { + *base = mBestBase; + return mBestBounds; + } + + virtual bool onIRect(const SkIRect& rect) + { + if (mBestBounds.isEmpty()) { + mBestBase = base(); + mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); + } + return false; + } + +protected: + int mBestBase; + SkIRect mBestBounds; +private: + typedef CommonCheck INHERITED; +}; + +class FindLast : public FindFirst { +public: + FindLast(int width, int height) + : INHERITED(width, height) + { + mBestBounds.setEmpty(); + } + + virtual bool onIRect(const SkIRect& rect) + { + mBestBase = base(); + mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); + return false; + } + +private: + typedef FindFirst INHERITED; }; +static bool baseLinesAgree(const SkIRect& rectA, int baseA, + const SkIRect& rectB, int baseB) +{ + return (rectA.fTop < baseB && rectA.fBottom >= baseB) + || (rectB.fTop < baseA && rectB.fBottom >= baseA); +} + class MultilineBuilder : public CommonCheck { public: - MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy, - SkRegion* region) - : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) { + MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end, + int endBase, const SkIRect& area, SkRegion* region) + : INHERITED(area.width(),area.height()) + , mCapture(false) + , mEnd(end) + , mEndBase(endBase) + , mFlipped(false) + , mSelectRegion(region) + , mStart(start) + , mStartBase(startBase) + { + mEnd.offset(-area.fLeft, -area.fTop); + mEndBase -= area.fTop; mLast.setEmpty(); mLastBase = INT_MAX; - mStart.offset(-dx, -dy); - mEnd.offset(-dx, -dy); + mStart.offset(-area.fLeft, -area.fTop); + mStartBase -= area.fTop; } - virtual bool onIRect(const SkIRect& rect) { - bool captureLast = false; - if ((rect.fLeft == mStart.fLeft && rect.fRight == mStart.fRight && - top() == mStart.fTop && bottom() == mStart.fBottom) || - (rect.fLeft == mEnd.fLeft && rect.fRight == mEnd.fRight && - top() == mEnd.fTop && bottom() == mEnd.fBottom)) { - captureLast = mCapture; - mCapture ^= true; + void finish() { + if (!mFlipped || SkIRect::Intersects(mLast, mSelectRect)) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)", + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); + mSelectRegion->op(mLast, SkRegion::kUnion_Op); } - if (mCapture || captureLast) { - SkIRect full; - full.set(rect.fLeft, top(), rect.fRight, bottom()); - if ((mLast.fTop < base() && mLast.fBottom >= base()) - || (mLastBase <= full.fBottom && mLastBase > full.fTop)) { - if (full.fLeft > mLast.fRight) - full.fLeft = mLast.fRight; - else if (full.fRight < mLast.fLeft) - full.fRight = mLast.fLeft; + } + + // return true if capture end was not found after capture begin + bool flipped() { + DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false"); + if (!mCapture) + return false; + mFlipped = true; + mSelectRect = mStart; + mSelectRect.join(mEnd); + mLast.setEmpty(); + mLastBase = INT_MAX; + mSelectRegion->setEmpty(); + return true; + } + + virtual bool onIRect(const SkIRect& rect) { + SkIRect full; + full.set(rect.fLeft, top(), rect.fRight, bottom()); + int fullBase = base(); + if (mFlipped) { + if (fullBase < mStart.fTop || fullBase > mEnd.fBottom + || (baseLinesAgree(mStart, mStartBase, full, fullBase) + && full.fLeft < mStart.fLeft) + || (baseLinesAgree(mEnd, mEndBase, full, fullBase) + && full.fRight > mEnd.fRight)) { + return false; + } + if (baseLinesAgree(mLast, mLastBase, full, fullBase) + && (full.fLeft - mLast.fRight < minSpaceWidth() * 3 + || (SkIRect::Intersects(full, mSelectRect) + && SkIRect::Intersects(mLast, mSelectRect)))) { + mLast.join(full); + return false; } - mSelectRegion->op(full, SkRegion::kUnion_Op); - DBG_NAV_LOGD("MultilineBuilder full=(%d,%d,r=%d,b=%d)", - full.fLeft, full.fTop, full.fRight, full.fBottom); + finish(); mLast = full; - mLastBase = base(); - if (mStart == mEnd) - mCapture = false; + mLastBase = fullBase; + return false; + } + if (full == mStart) + mCapture = true; + if (mCapture) { + if (baseLinesAgree(mLast, mLastBase, full, fullBase)) + mLast.join(full); + else { + finish(); + mLast = full; + mLastBase = fullBase; + } } + if (full == mEnd) + mCapture = false; return false; } -protected: - SkIRect mStart; + +protected: + bool mCapture; SkIRect mEnd; + int mEndBase; + bool mFlipped; SkIRect mLast; int mLastBase; + SkIRect mSelectRect; SkRegion* mSelectRegion; - bool mCapture; + SkIRect mStart; + int mStartBase; +private: + typedef CommonCheck INHERITED; }; -#define HYPHEN_MINUS 0x2D // ASCII hyphen -#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes -#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes +static inline bool compareBounds(const SkIRect* first, const SkIRect* second) +{ + return first->fTop < second->fTop; +} class TextExtractor : public CommonCheck { public: - TextExtractor(const SkRegion& region) : mSelectRegion(region), - mSkipFirstSpace(true) { // don't start with a space + TextExtractor(const SkIRect& start, int startBase, const SkIRect& end, + int endBase, const SkIRect& area, bool flipped) + : INHERITED(area.width(), area.height()) + , mEnd(end) + , mEndBase(endBase) + , mFlipped(flipped) + , mRecord(false) + , mSelectRect(start) + , mSkipFirstSpace(true) // don't start with a space + , mStart(start) + , mStartBase(startBase) + { + mEmpty.setEmpty(); + mEnd.offset(-area.fLeft, -area.fTop); + mEndBase -= area.fTop; + mLast.setEmpty(); + mLastBase = INT_MAX; + mSelectRect.join(end); + mSelectRect.offset(-area.fLeft, -area.fTop); + mStart.offset(-area.fLeft, -area.fTop); + mStartBase -= area.fTop; + } + + void addCharacter(const SkBounder::GlyphRec& rec) + { + if (!mSkipFirstSpace) { + if (addNewLine(rec)) { + DBG_NAV_LOG("write new line"); + *mSelectText.append() = '\n'; + *mSelectText.append() = '\n'; + } else if (addSpace(rec)) { + DBG_NAV_LOG("write space"); + *mSelectText.append() = ' '; + } + } else + mSkipFirstSpace = false; + recordGlyph(rec); + finishGlyph(); + if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID, + mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?'); + if (mLastUni) { + uint16_t chars[2]; + size_t count = SkUTF16_FromUnichar(mLastUni, chars); + *mSelectText.append() = chars[0]; + if (count == 2) + *mSelectText.append() = chars[1]; + } } - virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y, - const void* text) { - INHERITED::setUp(paint, matrix, y, text); - SkPaint charPaint = paint; - charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding); - mMinSpaceWidth = std::max(0, SkScalarToFixed( - charPaint.measureText(" ", 1)) - SK_Fixed1); + void finish() + { + Vector<SkIRect*> sortedBounds; + SkTDArray<uint16_t> temp; + int index; + DBG_NAV_LOGD("mSelectBounds.count=%d text=%d", mSelectBounds.count(), + mSelectText.count()); + for (index = 0; index < mSelectBounds.count(); index++) + sortedBounds.append(&mSelectBounds[index]); + std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds); + int lastEnd = -1; + for (index = 0; index < mSelectBounds.count(); index++) { + if (sortedBounds[index]->isEmpty()) + continue; + int order = sortedBounds[index] - &mSelectBounds[0]; + int start = order > 0 ? mSelectCount[order - 1] : 0; + int end = mSelectCount[order]; + DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end, + mSelectBounds[order].fTop); + int count = temp.count(); + if (count > 0 && temp[count - 1] != '\n' && start != lastEnd) { + // always separate paragraphs when original text is out of order + DBG_NAV_LOG("write new line"); + *temp.append() = '\n'; + *temp.append() = '\n'; + } + temp.append(end - start, &mSelectText[start]); + lastEnd = end; + } + mSelectText.swap(temp); } virtual bool onIRectGlyph(const SkIRect& rect, @@ -217,64 +779,85 @@ public: { SkIRect full; full.set(rect.fLeft, top(), rect.fRight, bottom()); - if (mSelectRegion.contains(full)) { - if (!mSkipFirstSpace && (mLastUni < HYPHEN || mLastUni > HORZ_BAR) - && mLastUni != HYPHEN_MINUS - && (mLastGlyph.fLSB.fY != rec.fLSB.fY // new baseline - || mLastGlyph.fLSB.fX > rec.fLSB.fX // glyphs are LTR - || mLastGlyph.fRSB.fX + mMinSpaceWidth < rec.fLSB.fX)) { - DBG_NAV_LOGD("TextExtractor append space" - " mLast=(%d,%d,r=%d,b=%d) mLastGlyph=((%g,%g),(%g,%g),%d)" - " full=(%d,%d,r=%d,b=%d) rec=((%g,%g),(%g,%g),%d)" - " mMinSpaceWidth=%g", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, - SkFixedToScalar(mLastGlyph.fLSB.fX), - SkFixedToScalar(mLastGlyph.fLSB.fY), - SkFixedToScalar(mLastGlyph.fRSB.fX), - SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID, - full.fLeft, full.fTop, full.fRight, full.fBottom, - SkFixedToScalar(rec.fLSB.fX), - SkFixedToScalar(rec.fLSB.fY), - SkFixedToScalar(rec.fRSB.fX), - SkFixedToScalar(rec.fRSB.fY), rec.fGlyphID, - SkFixedToScalar(mMinSpaceWidth)); - *mSelectText.append() = ' '; - } else - mSkipFirstSpace = false; - DBG_NAV_LOGD("TextExtractor [%02x] append full=(%d,%d,r=%d,b=%d)", - rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom); - SkPaint utfPaint = *mPaint; - utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &mLastUni); - if (mLastUni) { - uint16_t chars[2]; - size_t count = SkUTF16_FromUnichar(mLastUni, chars); - *mSelectText.append() = chars[0]; - if (count == 2) - *mSelectText.append() = chars[1]; + int fullBase = base(); + if (mFlipped) { + if (fullBase < mStart.fTop || fullBase > mEnd.fBottom + || (baseLinesAgree(mStart, mStartBase, full, fullBase) + && full.fLeft < mStart.fLeft) + || (baseLinesAgree(mEnd, mEndBase, full, fullBase) + && full.fRight > mEnd.fRight)) { + mSkipFirstSpace = true; + return false; + } + if (baseLinesAgree(mLast, mLastBase, full, fullBase) + && (full.fLeft - mLast.fRight < minSpaceWidth() * 3 + || (SkIRect::Intersects(full, mSelectRect) + && SkIRect::Intersects(mLast, mSelectRect)))) { + mLast.join(full); + addCharacter(rec); + return false; } + if (VERBOSE_LOGGING) DBG_NAV_LOGD("baseLinesAgree=%s" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mSelectRect=(%d,%d,r=%d,b=%d)", + baseLinesAgree(mLast, mLastBase, full, fullBase) ? "true" : "false", + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); + *mSelectBounds.append() = SkIRect::Intersects(mLast, mSelectRect) + ? mLast : mEmpty; + *mSelectCount.append() = mSelectText.count(); + addCharacter(rec); mLast = full; - mLastGlyph = rec; - } else { - mSkipFirstSpace = true; - DBG_NAV_LOGD("TextExtractor [%02x] skip full=(%d,%d,r=%d,b=%d)", - rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom); + mLastBase = fullBase; + return false; } + if (full == mStart) + mRecord = true; + if (mRecord) + addCharacter(rec); + else + mSkipFirstSpace = true; + if (full == mEnd) + mRecord = false; return false; } WebCore::String text() { + if (mFlipped) + finish(); + // the text has been copied in visual order. Reverse as needed if + // result contains right-to-left characters. + const uint16_t* start = mSelectText.begin(); + const uint16_t* end = mSelectText.end(); + while (start < end) { + SkUnichar ch = SkUTF16_NextUnichar(&start); + WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch); + if (WTF::Unicode::RightToLeftArabic == charDirection + || WTF::Unicode::RightToLeft == charDirection) { + WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count()); + break; + } + } return WebCore::String(mSelectText.begin(), mSelectText.count()); } protected: - const SkRegion& mSelectRegion; - SkTDArray<uint16_t> mSelectText; + SkIRect mEmpty; + SkIRect mEnd; + int mEndBase; + bool mFlipped; SkIRect mLast; - SkBounder::GlyphRec mLastGlyph; - SkUnichar mLastUni; - SkFixed mMinSpaceWidth; + int mLastBase; + bool mRecord; + SkTDArray<SkIRect> mSelectBounds; + SkTDArray<int> mSelectCount; + SkIRect mSelectRect; + SkTDArray<uint16_t> mSelectText; bool mSkipFirstSpace; + SkIRect mStart; + int mStartBase; private: typedef CommonCheck INHERITED; }; @@ -282,7 +865,7 @@ private: class TextCanvas : public SkCanvas { public: - TextCanvas(CommonCheck* bounder, const SkPicture& picture, const SkIRect& area) + TextCanvas(CommonCheck* bounder, const SkIRect& area) : mBounder(*bounder) { setBounder(bounder); SkBitmap bitmap; @@ -340,49 +923,201 @@ public: CommonCheck& mBounder; }; -void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area, - const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region) { +static bool buildSelection(const SkPicture& picture, const SkIRect& area, + const SkIRect& selStart, int startBase, + const SkIRect& selEnd, int endBase, SkRegion* region) +{ DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)" - " selEnd=(%d, %d, %d, %d)", + " selEnd=(%d, %d, %d, %d)", area.fLeft, area.fTop, area.fRight, area.fBottom, selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom, selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom); - MultilineBuilder builder(selStart, selEnd, area.fLeft, area.fTop, region); - TextCanvas checker(&builder, picture, area); + MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region); + TextCanvas checker(&builder, area); checker.drawPicture(const_cast<SkPicture&>(picture)); + bool flipped = builder.flipped(); + if (flipped) { + TextCanvas checker(&builder, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + } + builder.finish(); region->translate(area.fLeft, area.fTop); + return flipped; } -SkIRect CopyPaste::findClosest(const SkPicture& picture, const SkIRect& area, - int x, int y) { - FirstCheck _check(x - area.fLeft, y - area.fTop); - DBG_NAV_LOGD("area=(%d, %d, %d, %d) x=%d y=%d", area.fLeft, area.fTop, - area.fRight, area.fBottom, x, y); - TextCanvas checker(&_check, picture, area); +static SkIRect findClosest(FirstCheck& _check, const SkPicture& picture, + const SkIRect& area, int* base) +{ + DBG_NAV_LOGD("area=(%d, %d, %d, %d)", area.fLeft, area.fTop, + area.fRight, area.fBottom); + TextCanvas checker(&_check, area); checker.drawPicture(const_cast<SkPicture&>(picture)); - _check.offsetBounds(area.fLeft, area.fTop); - return _check.bestBounds(); + _check.finishGlyph(); + return _check.adjustedBounds(area, base); } -WebCore::String CopyPaste::text(const SkPicture& picture, const SkIRect& area, - const SkRegion& region) { - SkRegion copy = region; - copy.translate(-area.fLeft, -area.fTop); - const SkIRect& bounds = copy.getBounds(); - DBG_NAV_LOGD("area=(%d, %d, %d, %d) region=(%d, %d, %d, %d)", - area.fLeft, area.fTop, area.fRight, area.fBottom, - bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); - TextExtractor extractor(copy); - TextCanvas checker(&extractor, picture, area); +static SkIRect findEdge(const SkPicture& picture, const SkIRect& area, + int x, int y, bool left, int* base) +{ + SkIRect result; + result.setEmpty(); + FirstCheck center(x, y, area); + center.setRecordGlyph(); + int closestBase; + SkIRect closest = findClosest(center, picture, area, &closestBase); + closest.inset(-TOUCH_SLOP, -TOUCH_SLOP); + if (!closest.contains(x, y)) { + DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d", + closest.fLeft, closest.fTop, closest.fRight, closest.fBottom, + area.fLeft, area.fTop, area.fRight, area.fBottom, x, y); + return result; + } + EdgeCheck edge(x, y, area, center, left); + do { // detect left or right until there's a gap + DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d", + &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom); + TextCanvas checker(&edge, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + edge.finishGlyph(); + if (!edge.adjacent()) { + DBG_NAV_LOG("adjacent break"); + break; + } + int nextBase; + const SkIRect& next = edge.bestBounds(&nextBase); + if (next.isEmpty()) { + DBG_NAV_LOG("empty"); + break; + } + if (result == next) { + DBG_NAV_LOG("result == next"); + break; + } + *base = nextBase; + result = next; + edge.shiftStart(result); + } while (true); + if (!result.isEmpty()) { + *base += area.fTop; + result.offset(area.fLeft, area.fTop); + } + return result; +} + +static SkIRect findFirst(const SkPicture& picture, int* base) +{ + FindFirst finder(picture.width(), picture.height()); + SkIRect area; + area.set(0, 0, picture.width(), picture.height()); + TextCanvas checker(&finder, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + return finder.bestBounds(base); +} + +static SkIRect findLast(const SkPicture& picture, int* base) +{ + FindLast finder(picture.width(), picture.height()); + SkIRect area; + area.set(0, 0, picture.width(), picture.height()); + TextCanvas checker(&finder, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + return finder.bestBounds(base); +} + +static SkIRect findLeft(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base) +{ + return findEdge(picture, area, x, y, true, base); +} + +static SkIRect findRight(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base) +{ + return findEdge(picture, area, x, y, false, base); +} + +static WebCore::String text(const SkPicture& picture, const SkIRect& area, + const SkIRect& start, int startBase, const SkIRect& end, + int endBase, bool flipped) +{ + TextExtractor extractor(start, startBase, end, endBase, area, flipped); + TextCanvas checker(&extractor, area); checker.drawPicture(const_cast<SkPicture&>(picture)); return extractor.text(); } +#define CONTROL_OFFSET 3 +#define CONTROL_NOTCH 9 +#define CONTROL_HEIGHT 18 +#define CONTROL_WIDTH 12 +#define STROKE_WIDTH 0.4f +#define SLOP 20 + +SelectText::SelectText() +{ + reset(); + SkScalar innerW = CONTROL_WIDTH - STROKE_WIDTH; + SkScalar innerH = CONTROL_HEIGHT - STROKE_WIDTH; + SkPaint paint; + paint.setAntiAlias(true); + paint.setStrokeWidth(STROKE_WIDTH); + + SkPath startPath; + startPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH); + startPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT); + startPath.lineTo(0, CONTROL_HEIGHT); + startPath.lineTo(0, CONTROL_OFFSET); + startPath.close(); + + SkCanvas* canvas = m_startControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT); + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(0xD077A14B); + canvas->drawPath(startPath, paint); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(0x40000000); + canvas->drawLine(-innerW, CONTROL_NOTCH, -innerW, innerH, paint); + canvas->drawLine(-innerW + STROKE_WIDTH, innerH, -STROKE_WIDTH, innerH, paint); + paint.setColor(0x40ffffff); + canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH, + -CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint); + canvas->drawLine(-STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, + -STROKE_WIDTH, innerH, paint); + paint.setColor(0xffaaaaaa); + canvas->drawPath(startPath, paint); + m_startControl.endRecording(); + + SkPath endPath; + endPath.moveTo(0, CONTROL_OFFSET); + endPath.lineTo(0, CONTROL_HEIGHT); + endPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT); + endPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH); + endPath.close(); + + canvas = m_endControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT); + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(0xD077A14B); + canvas->drawPath(endPath, paint); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(0x40000000); + canvas->drawLine(STROKE_WIDTH, CONTROL_OFFSET + STROKE_WIDTH, + STROKE_WIDTH, innerH, paint); + canvas->drawLine(STROKE_WIDTH + STROKE_WIDTH, innerH, innerW, innerH, paint); + paint.setColor(0x40ffffff); + canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH, + CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint); + canvas->drawLine(STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, + STROKE_WIDTH, innerH, paint); + paint.setColor(0xffaaaaaa); + canvas->drawPath(endPath, paint); + m_endControl.endRecording(); +} + void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer) { - if (layer->picture() != m_picture) - return; - if (m_drawRegion) + // FIXME: layer may not own the original selected picture + m_picture = layer->picture(); + DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d", m_extendSelection, m_drawPointer); + if (m_extendSelection) drawSelectionRegion(canvas); if (m_drawPointer) drawSelectionPointer(canvas); @@ -406,7 +1141,7 @@ void SelectText::drawSelectionPointer(SkCanvas* canvas) paint.setStrokeWidth(SK_Scalar1 * 2); int sc = canvas->save(); canvas->scale(m_inverseScale, m_inverseScale); - canvas->translate(SkIntToScalar(m_selectX), SkIntToScalar(m_selectY)); + canvas->translate(m_selectX, m_selectY); canvas->drawPath(path, paint); if (!m_extendSelection) { paint.setStyle(SkPaint::kFill_Style); @@ -424,19 +1159,92 @@ void SelectText::drawSelectionRegion(SkCanvas* canvas) return; SkIRect ivisBounds; visBounds.round(&ivisBounds); - CopyPaste::buildSelection(*m_picture, ivisBounds, m_selStart, m_selEnd, - &m_selRegion); + ivisBounds.join(m_selStart); + ivisBounds.join(m_selEnd); + DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", + m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, + m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); + m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase, + m_selEnd, m_endBase, &m_selRegion); SkPath path; m_selRegion.getBoundaryPath(&path); + path.setFillType(SkPath::kEvenOdd_FillType); + SkPaint paint; paint.setAntiAlias(true); - paint.setColor(SkColorSetARGB(0x40, 255, 51, 204)); + paint.setColor(SkColorSetARGB(0x80, 151, 200, 73)); canvas->drawPath(path, paint); + // experiment to draw touchable controls that resize the selection + canvas->save(); + canvas->translate(m_selStart.fLeft, m_selStart.fBottom); + canvas->drawPicture(m_startControl); + canvas->restore(); + canvas->save(); + canvas->translate(m_selEnd.fRight, m_selEnd.fBottom); + canvas->drawPicture(m_endControl); + canvas->restore(); +} + +void SelectText::extendSelection(const SkPicture* picture, int x, int y) +{ + SkIRect clipRect = m_visibleRect; + int base; + if (m_startSelection) { + if (!clipRect.contains(x, y) + || !clipRect.contains(m_original.fX, m_original.fY)) { + clipRect.set(m_original.fX, m_original.fY, x, y); + clipRect.sort(); + clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height()); + } + DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft, + clipRect.fTop, clipRect.fRight, clipRect.fBottom); + m_picture = picture; + FirstCheck center(m_original.fX, m_original.fY, clipRect); + m_selStart = m_selEnd = findClosest(center, *picture, clipRect, &base); + m_startBase = m_endBase = base; + m_startSelection = false; + m_extendSelection = true; + m_original.fX = m_original.fY = 0; + } else if (picture != m_picture) + return; + x -= m_original.fX; + y -= m_original.fY; + if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) { + clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y); + clipRect.sort(); + clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height()); + } + DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d)", clipRect.fLeft, + clipRect.fTop, clipRect.fRight, clipRect.fBottom); + FirstCheck extension(x, y, clipRect); + SkIRect found = findClosest(extension, *picture, clipRect, &base); + DBG_NAV_LOGD("pic=%p x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)" + " m_extendSelection=%s", + picture, x, y, m_startSelection ? "true" : "false", + m_hitTopLeft ? "m_selStart" : "m_selEnd", + found.fLeft, found.fTop, found.fRight, found.fBottom, + m_extendSelection ? "true" : "false"); + if (m_hitTopLeft) { + m_startBase = base; + m_selStart = found; + } else { + m_endBase = base; + m_selEnd = found; + } + swapAsNeeded(); } const String SelectText::getSelection() { - String result = CopyPaste::text(*m_picture, m_visibleRect, m_selRegion); + SkIRect clipRect; + clipRect.set(0, 0, m_picture->width(), m_picture->height()); + String result = text(*m_picture, clipRect, m_selStart, m_startBase, + m_selEnd, m_endBase, m_flipped); + DBG_NAV_LOGD("clip=(%d,%d,%d,%d)" + " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", + clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, + m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, + m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString return result; } @@ -447,35 +1255,173 @@ void SelectText::getSelectionArrow(SkPath* path) 0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11 }; for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2) - path->lineTo(SkIntToScalar(arrow[index]), SkIntToScalar(arrow[index + 1])); + path->lineTo(arrow[index], arrow[index + 1]); path->close(); } void SelectText::getSelectionCaret(SkPath* path) { - SkScalar height = SkIntToScalar(m_selStart.fBottom - m_selStart.fTop); + SkScalar height = m_selStart.fBottom - m_selStart.fTop; SkScalar dist = height / 4; path->moveTo(0, -height / 2); path->rLineTo(0, height); path->rLineTo(-dist, dist); - path->rMoveTo(0, -SK_Scalar1/2); + path->rMoveTo(0, -0.5f); path->rLineTo(dist * 2, 0); - path->rMoveTo(0, SK_Scalar1/2); + path->rMoveTo(0, 0.5f); path->rLineTo(-dist, -dist); } -void SelectText::moveSelection(const SkPicture* picture, int x, int y, - bool extendSelection) +bool SelectText::hitCorner(int cx, int cy, int x, int y) const +{ + SkIRect test; + test.set(cx, cy, cx, cy); + test.inset(-SLOP, -SLOP); + return test.contains(x, y); +} + +bool SelectText::hitSelection(int x, int y) const +{ + int left = m_selStart.fLeft - CONTROL_WIDTH / 2; + int top = m_selStart.fBottom + CONTROL_HEIGHT / 2; + if (hitCorner(left, top, x, y)) + return true; + int right = m_selEnd.fRight + CONTROL_WIDTH / 2; + int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2; + if (hitCorner(right, bottom, x, y)) + return true; + return m_selRegion.contains(x, y); +} + +void SelectText::moveSelection(const SkPicture* picture, int x, int y) { - if (!extendSelection) + SkIRect clipRect = m_visibleRect; + clipRect.join(m_selStart); + clipRect.join(m_selEnd); + if (!m_extendSelection) m_picture = picture; - m_selEnd = CopyPaste::findClosest(*picture, m_visibleRect, x, y); - if (!extendSelection) - m_selStart = m_selEnd; + FirstCheck center(x, y, clipRect); + int base; + SkIRect found = findClosest(center, *picture, clipRect, &base); + if (m_hitTopLeft || !m_extendSelection) { + m_startBase = base; + m_selStart = found; + } + if (!m_hitTopLeft || !m_extendSelection) { + m_endBase = base; + m_selEnd = found; + } + swapAsNeeded(); DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)" - " m_selEnd=(%d, %d, %d, %d)", x, y, extendSelection ? "true" : "false", + " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false", + m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, + m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); +} + +void SelectText::reset() +{ + DBG_NAV_LOG("m_extendSelection=false"); + m_selStart.setEmpty(); + m_selEnd.setEmpty(); + m_extendSelection = false; + m_startSelection = false; +} + +void SelectText::selectAll(const SkPicture* picture) +{ + m_selStart = findFirst(*picture, &m_startBase); + m_selEnd = findLast(*picture, &m_endBase); + m_extendSelection = true; +} + +int SelectText::selectionX() const +{ + return m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight; +} + +int SelectText::selectionY() const +{ + const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd; + return (rect.fTop + rect.fBottom) >> 1; +} + +bool SelectText::startSelection(int x, int y) +{ + m_original.fX = x; + m_original.fY = y; + if (m_selStart.isEmpty()) { + DBG_NAV_LOGD("empty start x=%d y=%d", x, y); + m_startSelection = true; + return true; + } + int left = m_selStart.fLeft - CONTROL_WIDTH / 2; + int top = m_selStart.fBottom + CONTROL_HEIGHT / 2; + m_hitTopLeft = hitCorner(left, top, x, y); + int right = m_selEnd.fRight + CONTROL_WIDTH / 2; + int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2; + bool hitBottomRight = hitCorner(right, bottom, x, y); + DBG_NAV_LOGD("left=%d top=%d right=%d bottom=%d x=%d y=%d", left, top, + right, bottom, x, y); + if (m_hitTopLeft && (!hitBottomRight || y - top < bottom - y)) { + DBG_NAV_LOG("hit top left"); + m_original.fX -= left; + m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1; + } else if (hitBottomRight) { + DBG_NAV_LOG("hit bottom right"); + m_original.fX -= right; + m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1; + } + return m_hitTopLeft || hitBottomRight; +} + +/* selects the word at (x, y) +* a word is normally delimited by spaces +* a string of digits (even with inside spaces) is a word (for phone numbers) +* FIXME: digit find isn't implemented yet +* returns true if a word was selected +*/ +bool SelectText::wordSelection(const SkPicture* picture) +{ + int x = m_selStart.fLeft; + int y = (m_selStart.fTop + m_selStart.fBottom) >> 1; + SkIRect clipRect = m_visibleRect; + clipRect.fLeft -= m_visibleRect.width() >> 1; + int base; + SkIRect left = findLeft(*picture, clipRect, x, y, &base); + if (!left.isEmpty()) { + m_startBase = base; + m_selStart = left; + } + x = m_selEnd.fRight; + y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1; + clipRect = m_visibleRect; + clipRect.fRight += m_visibleRect.width() >> 1; + SkIRect right = findRight(*picture, clipRect, x, y, &base); + if (!right.isEmpty()) { + m_endBase = base; + m_selEnd = right; + } + DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); + if (!left.isEmpty() || !right.isEmpty()) { + m_extendSelection = true; + return true; + } + return false; +} + +void SelectText::swapAsNeeded() +{ + if (m_selStart.fTop >= m_selEnd.fBottom + || (m_selStart.fBottom > m_selEnd.fTop + && m_selStart.fRight > m_selEnd.fLeft)) + { + SkTSwap(m_startBase, m_endBase); + SkTSwap(m_selStart, m_selEnd); + m_hitTopLeft ^= true; + DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false"); + } } } diff --git a/WebKit/android/nav/SelectText.h b/WebKit/android/nav/SelectText.h index 8174046..404e9e7 100644 --- a/WebKit/android/nav/SelectText.h +++ b/WebKit/android/nav/SelectText.h @@ -29,6 +29,7 @@ #include "DrawExtra.h" #include "IntRect.h" #include "PlatformString.h" +#include "SkPath.h" class SkPicture; struct SkIRect; @@ -38,47 +39,57 @@ namespace android { class CachedRoot; -class CopyPaste { -public: - static void buildSelection(const SkPicture& , const SkIRect& area, - const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region); - static SkIRect findClosest(const SkPicture& , const SkIRect& area, - int x, int y); - static String text(const SkPicture& , const SkIRect& area, - const SkRegion& ); -}; - class SelectText : public DrawExtra { public: - SelectText() { - m_selStart.setEmpty(); - m_selEnd.setEmpty(); - } + SelectText(); virtual void draw(SkCanvas* , LayerAndroid* ); + void extendSelection(const SkPicture* , int x, int y); const String getSelection(); - void moveSelection(const SkPicture* , int x, int y, bool extendSelection); + bool hitSelection(int x, int y) const; + void moveSelection(const SkPicture* , int x, int y); + void reset(); + void selectAll(const SkPicture* ); + int selectionX() const; + int selectionY() const; void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; } - void setDrawRegion(bool drawRegion) { m_drawRegion = drawRegion; } + void setExtendSelection(bool extend) { m_extendSelection = extend; } void setVisibleRect(const IntRect& rect) { m_visibleRect = rect; } + bool startSelection(int x, int y); + bool wordSelection(const SkPicture* picture); +public: + float m_inverseScale; // inverse scale, x, y used for drawing select path + int m_selectX; + int m_selectY; private: - friend class WebView; void drawSelectionPointer(SkCanvas* ); void drawSelectionRegion(SkCanvas* ); static void getSelectionArrow(SkPath* ); void getSelectionCaret(SkPath* ); + bool hitCorner(int cx, int cy, int x, int y) const; + void swapAsNeeded(); + SkIPoint m_original; // computed start of extend selection SkIRect m_selStart; SkIRect m_selEnd; - SkIRect m_visibleRect; - SkRegion m_selRegion; + int m_startBase; + int m_endBase; + SkIRect m_visibleRect; // constrains picture computations to visible area + SkRegion m_selRegion; // computed from sel start, end + SkPicture m_startControl; + SkPicture m_endControl; const SkPicture* m_picture; - float m_inverseScale; - int m_selectX; - int m_selectY; - bool m_drawRegion; bool m_drawPointer; - bool m_extendSelection; + bool m_extendSelection; // false when trackball is moving pointer + bool m_flipped; + bool m_hitTopLeft; + bool m_startSelection; }; } +namespace WebCore { + +void ReverseBidi(UChar* chars, int len); + +} + #endif diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index 2ed6148..5154b42 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -30,10 +30,10 @@ #include "AndroidAnimation.h" #include "AndroidLog.h" #include "AtomicString.h" +#include "BaseLayerAndroid.h" #include "CachedFrame.h" #include "CachedNode.h" #include "CachedRoot.h" -#include "CString.h" #include "DrawExtra.h" #include "FindCanvas.h" #include "Frame.h" @@ -68,6 +68,7 @@ #include <JNIHelp.h> #include <jni.h> #include <ui/KeycodeLabels.h> +#include <wtf/text/CString.h> namespace android { @@ -177,7 +178,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : m_lastDx = 0; m_lastDxTime = 0; m_ringAnimationEnd = 0; - m_rootLayer = 0; + m_baseLayer = 0; } ~WebView() @@ -190,7 +191,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : } delete m_frameCacheUI; delete m_navPictureUI; - delete m_rootLayer; + delete m_baseLayer; } WebViewCore* getWebViewCore() const { @@ -303,10 +304,11 @@ void scrollRectOnScreen(const IntRect& rect) SkRect visible; calcOurContentVisibleRect(&visible); #if USE(ACCELERATED_COMPOSITING) - if (m_rootLayer) { - m_rootLayer->updateFixedLayersPositions(visible); - m_rootLayer->updatePositions(); - visible = m_rootLayer->subtractLayers(visible); + LayerAndroid* root = compositeRoot(); + if (root) { + root->updateFixedLayersPositions(visible); + root->updatePositions(); + visible = root->subtractLayers(visible); } #endif int dx = 0; @@ -394,14 +396,30 @@ void drawCursorPostamble() } } -void drawExtras(SkCanvas* canvas, int extras) +PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) { + PictureSet* ret = 0; + if (!m_baseLayer) { + canvas->drawColor(bgColor); + return ret; + } + + // draw the content of the base layer first + PictureSet* content = m_baseLayer->content(); + int sc = canvas->save(SkCanvas::kClip_SaveFlag); + canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(), + content->height()), SkRegion::kDifference_Op); + canvas->drawColor(bgColor); + canvas->restoreToCount(sc); + if (content->draw(canvas)) + ret = split ? new PictureSet(*content) : 0; + CachedRoot* root = getFrameCache(AllowNewer); if (!root) { DBG_NAV_LOG("!root"); if (extras == DrawExtrasCursorRing) resetCursorRing(); - return; + return ret; } LayerAndroid mainPicture(m_navPictureUI); DrawExtra* extra = 0; @@ -425,22 +443,24 @@ void drawExtras(SkCanvas* canvas, int extras) if (extra) extra->draw(canvas, &mainPicture); #if USE(ACCELERATED_COMPOSITING) - if (!m_rootLayer) - return; - m_rootLayer->setExtra(extra); + LayerAndroid* compositeLayer = compositeRoot(); + if (!compositeLayer) + return ret; + compositeLayer->setExtra(extra); SkRect visible; calcOurContentVisibleRect(&visible); // call this to be sure we've adjusted for any scrolling or animations // before we actually draw - m_rootLayer->updateFixedLayersPositions(visible); - m_rootLayer->updatePositions(); - // We have to set the canvas' matrix on the root layer + compositeLayer->updateFixedLayersPositions(visible); + compositeLayer->updatePositions(); + // We have to set the canvas' matrix on the base layer // (to have fixed layers work as intended) SkAutoCanvasRestore restore(canvas, true); - m_rootLayer->setMatrix(canvas->getTotalMatrix()); + m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); - m_rootLayer->draw(canvas); + m_baseLayer->draw(canvas); #endif + return ret; } @@ -565,7 +585,7 @@ CachedRoot* getFrameCache(FrameCachePermission allowNewer) m_viewImpl->m_navPictureKit = 0; m_viewImpl->gFrameCacheMutex.unlock(); if (m_frameCacheUI) - m_frameCacheUI->setRootLayer(m_rootLayer); + m_frameCacheUI->setRootLayer(compositeRoot()); #if USE(ACCELERATED_COMPOSITING) if (layerId >= 0) { SkRect visible; @@ -892,7 +912,8 @@ bool motionUp(int x, int y, int slop) viewInvalidate(); if (!result->isTextInput()) { clearTextEntry(); - setFollowedLink(true); + if (!result->isSelect() && !result->isContentEditable()) + setFollowedLink(true); if (syntheticLink) overrideUrlLoading(result->getExport()); } @@ -946,7 +967,7 @@ String getSelection() return m_selectText.getSelection(); } -void moveSelection(int x, int y, bool extendSelection) +void moveSelection(int x, int y) { const CachedRoot* root = getFrameCache(DontAllowNewer); if (!root) @@ -957,26 +978,81 @@ void moveSelection(int x, int y, bool extendSelection) IntRect visibleRect; getVisibleRect(&visibleRect); m_selectText.setVisibleRect(visibleRect); - m_selectText.moveSelection(picture, x, y, extendSelection); + m_selectText.moveSelection(picture, x, y); +} + +void selectAll() +{ + const CachedRoot* root = getFrameCache(DontAllowNewer); + if (!root) + return; + SkPicture* picture = root->pictureAt(0, 0); + m_selectText.selectAll(picture); +} + +int selectionX() +{ + return m_selectText.selectionX(); } -void setSelectionPointer(bool set, float scale, int x, int y, - bool extendSelection) +int selectionY() +{ + return m_selectText.selectionY(); +} + +void resetSelection() +{ + m_selectText.reset(); +} + +bool startSelection(int x, int y) +{ + return m_selectText.startSelection(x, y); +} + +bool wordSelection(int x, int y) +{ + startSelection(x, y); + if (!extendSelection(x, y)) + return false; + m_selectText.setDrawPointer(false); + SkPicture* picture = getFrameCache(DontAllowNewer)->pictureAt(x, y); + return m_selectText.wordSelection(picture); +} + +bool extendSelection(int x, int y) +{ + const CachedRoot* root = getFrameCache(DontAllowNewer); + if (!root) + return false; + SkPicture* picture = root->pictureAt(x, y); + IntRect visibleRect; + getVisibleRect(&visibleRect); + m_selectText.setVisibleRect(visibleRect); + m_selectText.extendSelection(picture, x, y); + return true; +} + +bool hitSelection(int x, int y) +{ + return m_selectText.hitSelection(x, y); +} + +void setExtendSelection() +{ + m_selectText.setExtendSelection(true); +} + +void setSelectionPointer(bool set, float scale, int x, int y) { m_selectText.setDrawPointer(set); if (!set) return; - m_selectText.m_extendSelection = extendSelection; m_selectText.m_inverseScale = scale; m_selectText.m_selectX = x; m_selectText.m_selectY = y; } -void setSelectionRegion(bool set) -{ - m_selectText.setDrawRegion(set); -} - void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr) { DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr); @@ -1035,6 +1111,11 @@ void setMatches(WTF::Vector<MatchInfo>* matches) viewInvalidate(); } +int currentMatchIndex() +{ + return m_findOnPage.currentMatchIndex(); +} + bool scrollBy(int dx, int dy) { LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); @@ -1109,20 +1190,49 @@ int moveGeneration() return m_viewImpl->m_moveGeneration; } -LayerAndroid* rootLayer() const +LayerAndroid* compositeRoot() const { - return m_rootLayer; + LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1, + "base layer can't have more than one child %s", __FUNCTION__); + if (m_baseLayer && m_baseLayer->countChildren() == 1) + return static_cast<LayerAndroid*>(m_baseLayer->getChild(0)); + else + return 0; } -void setRootLayer(LayerAndroid* layer) +void setBaseLayer(BaseLayerAndroid* layer) { - delete m_rootLayer; - m_rootLayer = layer; + delete m_baseLayer; + m_baseLayer = layer; CachedRoot* root = getFrameCache(DontAllowNewer); if (!root) return; root->resetLayers(); - root->setRootLayer(m_rootLayer); + root->setRootLayer(compositeRoot()); +} + +void replaceBaseContent(PictureSet* set) +{ + if (!m_baseLayer) + return; + m_baseLayer->setContent(*set); + delete set; +} + +void copyBaseContentToPicture(SkPicture* picture) +{ + if (!m_baseLayer) + return; + PictureSet* content = m_baseLayer->content(); + content->draw(picture->beginRecording(content->width(), content->height(), + SkPicture::kUsePathBoundsForClip_RecordingFlag)); + picture->endRecording(); +} + +bool hasContent() { + if (!m_baseLayer) + return false; + return !m_baseLayer->content()->isEmpty(); } private: // local state for WebView @@ -1139,7 +1249,7 @@ private: // local state for WebView SelectText m_selectText; FindOnPage m_findOnPage; CursorRing m_ring; - LayerAndroid* m_rootLayer; + BaseLayerAndroid* m_baseLayer; }; // end of WebView class /* @@ -1272,6 +1382,19 @@ static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj) return cursor ? frame->textInput(cursor) : 0; } +static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj) +{ + const CachedNode* focus = getFocusNode(env, obj); + if (!focus) return false; + // Plugins handle shift and arrows whether or not they have focus. + if (focus->isPlugin()) return true; + const CachedNode* cursor = getCursorNode(env, obj); + // ContentEditable nodes should only receive shift and arrows if they have + // both the cursor and the focus. + return cursor && cursor->nodePointer() == focus->nodePointer() + && cursor->isContentEditable(); +} + static jboolean nativeCursorMatchesFocus(JNIEnv *env, jobject obj) { const CachedNode* cursor = getCursorNode(env, obj); @@ -1357,28 +1480,43 @@ static void nativeDebugDump(JNIEnv *env, jobject obj) #endif } -static void nativeDrawExtras(JNIEnv *env, jobject obj, jobject canv, jint extras) -{ +static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, jint color, + jint extras, jboolean split) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); - GET_NATIVE_VIEW(env, obj)->drawExtras(canvas, extras); + return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split)); } static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj) { #if USE(ACCELERATED_COMPOSITING) - const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer(); + const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot(); if (root) return root->evaluateAnimations(); #endif return false; } -static void nativeSetRootLayer(JNIEnv *env, jobject obj, jint layer) +static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer) { -#if USE(ACCELERATED_COMPOSITING) - LayerAndroid* layerImpl = reinterpret_cast<LayerAndroid*>(layer); - GET_NATIVE_VIEW(env, obj)->setRootLayer(layerImpl); -#endif + BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer); + GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl); +} + +static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content) +{ + PictureSet* set = reinterpret_cast<PictureSet*>(content); + GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set); +} + +static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict) +{ + SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); + GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture); +} + +static bool nativeHasContent(JNIEnv *env, jobject obj) +{ + return GET_NATIVE_VIEW(env, obj)->hasContent(); } static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y) @@ -1568,7 +1706,7 @@ static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect) { SkIRect irect = jrect_to_webrect(env, jrect); #if USE(ACCELERATED_COMPOSITING) - LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer(); + LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot(); if (root) { SkRect rect; rect.set(irect); @@ -1644,9 +1782,12 @@ static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj) static void nativeSetFollowedLink(JNIEnv *env, jobject obj, bool followed) { - WebView* view = GET_NATIVE_VIEW(env, obj); - LOG_ASSERT(view, "view not set in %s", __FUNCTION__); - view->setFollowedLink(followed); + const CachedNode* cursor = getCursorNode(env, obj); + if (cursor && !cursor->isSelect() && ! cursor->isContentEditable()) { + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->setFollowedLink(followed); + } } static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure) @@ -1735,6 +1876,13 @@ static void nativeFindNext(JNIEnv *env, jobject obj, bool forward) view->findNext(forward); } +static int nativeFindIndex(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in nativeFindIndex"); + return view->currentMatchIndex(); +} + static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation) { WebView* view = GET_NATIVE_VIEW(env, obj); @@ -1805,11 +1953,39 @@ static int nativeMoveGeneration(JNIEnv *env, jobject obj) return view->moveGeneration(); } -static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y, bool ex) +static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y) { - WebView* view = GET_NATIVE_VIEW(env, obj); - LOG_ASSERT(view, "view not set in %s", __FUNCTION__); - view->moveSelection(x, y, ex); + GET_NATIVE_VIEW(env, obj)->moveSelection(x, y); +} + +static void nativeResetSelection(JNIEnv *env, jobject obj) +{ + return GET_NATIVE_VIEW(env, obj)->resetSelection(); +} + +static void nativeSelectAll(JNIEnv* env, jobject obj) +{ + GET_NATIVE_VIEW(env, obj)->selectAll(); +} + +static void nativeSetExtendSelection(JNIEnv *env, jobject obj) +{ + GET_NATIVE_VIEW(env, obj)->setExtendSelection(); +} + +static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y) +{ + return GET_NATIVE_VIEW(env, obj)->startSelection(x, y); +} + +static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y) +{ + return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y); +} + +static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y) +{ + GET_NATIVE_VIEW(env, obj)->extendSelection(x, y); } static jobject nativeGetSelection(JNIEnv *env, jobject obj) @@ -1820,15 +1996,25 @@ static jobject nativeGetSelection(JNIEnv *env, jobject obj) return env->NewString((jchar*)selection.characters(), selection.length()); } -static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set, - jfloat scale, jint x, jint y, bool ex) +static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y) { - GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y, ex); + return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y); } -static void nativeSetSelectionRegion(JNIEnv *env, jobject obj, jboolean set) +static jint nativeSelectionX(JNIEnv *env, jobject obj) { - GET_NATIVE_VIEW(env, obj)->setSelectionRegion(set); + return GET_NATIVE_VIEW(env, obj)->selectionX(); +} + +static jint nativeSelectionY(JNIEnv *env, jobject obj) +{ + return GET_NATIVE_VIEW(env, obj)->selectionY(); +} + +static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set, + jfloat scale, jint x, jint y) +{ + GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y); } #ifdef ANDROID_DUMP_DISPLAY_TREE @@ -1859,26 +2045,13 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) SkDumpCanvas canvas(&dumper); // this will playback the picture into the canvas, which will // spew its contents to the dumper - view->getWebViewCore()->drawContent(&canvas, 0); -#if USE(ACCELERATED_COMPOSITING) - if (true) { - LayerAndroid* rootLayer = view->rootLayer(); - if (rootLayer) { - // We have to set the canvas' matrix on the root layer - // (to have fixed layers work as intended) - SkAutoCanvasRestore restore(&canvas, true); - rootLayer->setMatrix(canvas.getTotalMatrix()); - canvas.resetMatrix(); - rootLayer->draw(&canvas); - } - } -#endif + view->draw(&canvas, 0, 0, false); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); } #if USE(ACCELERATED_COMPOSITING) - const LayerAndroid* rootLayer = view->rootLayer(); + const LayerAndroid* rootLayer = view->compositeRoot(); if (rootLayer) { FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w"); if (file) { @@ -1907,6 +2080,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCreate }, { "nativeCursorFramePointer", "()I", (void*) nativeCursorFramePointer }, + { "nativePageShouldHandleShiftAndArrows", "()Z", + (void*) nativePageShouldHandleShiftAndArrows }, { "nativeCursorMatchesFocus", "()Z", (void*) nativeCursorMatchesFocus }, { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;", @@ -1929,16 +2104,20 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeDebugDump }, { "nativeDestroy", "()V", (void*) nativeDestroy }, - { "nativeDrawExtras", "(Landroid/graphics/Canvas;I)V", - (void*) nativeDrawExtras }, + { "nativeDraw", "(Landroid/graphics/Canvas;IIZ)I", + (void*) nativeDraw }, { "nativeDumpDisplayTree", "(Ljava/lang/String;)V", (void*) nativeDumpDisplayTree }, { "nativeEvaluateLayersAnimations", "()Z", (void*) nativeEvaluateLayersAnimations }, + { "nativeExtendSelection", "(II)V", + (void*) nativeExtendSelection }, { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) nativeFindAll }, { "nativeFindNext", "(Z)V", (void*) nativeFindNext }, + { "nativeFindIndex", "()I", + (void*) nativeFindIndex}, { "nativeFocusCandidateFramePointer", "()I", (void*) nativeFocusCandidateFramePointer }, { "nativeFocusCandidateHasNextTextfield", "()Z", @@ -1979,6 +2158,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeHasFocusNode }, { "nativeHideCursor", "()V", (void*) nativeHideCursor }, + { "nativeHitSelection", "(II)Z", + (void*) nativeHitSelection }, { "nativeImageURI", "(II)Ljava/lang/String;", (void*) nativeImageURI }, { "nativeInstrumentReport", "()V", @@ -1991,14 +2172,24 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeMoveCursorToNextTextInput }, { "nativeMoveGeneration", "()I", (void*) nativeMoveGeneration }, - { "nativeMoveSelection", "(IIZ)V", + { "nativeMoveSelection", "(II)V", (void*) nativeMoveSelection }, { "nativePointInNavCache", "(III)Z", (void*) nativePointInNavCache }, { "nativeRecordButtons", "(ZZZ)V", (void*) nativeRecordButtons }, + { "nativeResetSelection", "()V", + (void*) nativeResetSelection }, + { "nativeSelectAll", "()V", + (void*) nativeSelectAll }, { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V", (void*) nativeSelectBestAt }, + { "nativeSelectionX", "()I", + (void*) nativeSelectionX }, + { "nativeSelectionY", "()I", + (void*) nativeSelectionY }, + { "nativeSetExtendSelection", "()V", + (void*) nativeSetExtendSelection }, { "nativeSetFindIsEmpty", "()V", (void*) nativeSetFindIsEmpty }, { "nativeSetFindIsUp", "(Z)V", @@ -2007,18 +2198,26 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSetFollowedLink }, { "nativeSetHeightCanMeasure", "(Z)V", (void*) nativeSetHeightCanMeasure }, - { "nativeSetRootLayer", "(I)V", - (void*) nativeSetRootLayer }, - { "nativeSetSelectionPointer", "(ZFIIZ)V", + { "nativeSetBaseLayer", "(I)V", + (void*) nativeSetBaseLayer }, + { "nativeReplaceBaseContent", "(I)V", + (void*) nativeReplaceBaseContent }, + { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", + (void*) nativeCopyBaseContentToPicture }, + { "nativeHasContent", "()Z", + (void*) nativeHasContent }, + { "nativeSetSelectionPointer", "(ZFII)V", (void*) nativeSetSelectionPointer }, - { "nativeSetSelectionRegion", "(Z)V", - (void*) nativeSetSelectionRegion }, + { "nativeStartSelection", "(II)Z", + (void*) nativeStartSelection }, { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;", (void*) nativeSubtractLayers }, { "nativeTextGeneration", "()I", (void*) nativeTextGeneration }, { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V", (void*) nativeUpdateCachedTextfield }, + { "nativeWordSelection", "(II)Z", + (void*) nativeWordSelection }, { "nativeGetBlockLeftEdge", "(IIF)I", (void*) nativeGetBlockLeftEdge }, }; diff --git a/WebKit/android/plugins/ANPSystemInterface.cpp b/WebKit/android/plugins/ANPSystemInterface.cpp index 42ec9e4..34fe162 100644 --- a/WebKit/android/plugins/ANPSystemInterface.cpp +++ b/WebKit/android/plugins/ANPSystemInterface.cpp @@ -26,7 +26,7 @@ // must include config.h first for webkit to fiddle with new/delete #include "config.h" -#include "CString.h" +#include "ANPSystem_npapi.h" #include "JavaSharedClient.h" #include "PluginClient.h" #include "PluginPackage.h" @@ -34,8 +34,7 @@ #include "PluginWidgetAndroid.h" #include "SkString.h" #include "WebViewCore.h" - -#include "ANPSystem_npapi.h" +#include <wtf/text/CString.h> static const char* gApplicationDataDir = NULL; diff --git a/WebKit/android/plugins/PluginDebugAndroid.cpp b/WebKit/android/plugins/PluginDebugAndroid.cpp index e13e928..0388d4b 100644 --- a/WebKit/android/plugins/PluginDebugAndroid.cpp +++ b/WebKit/android/plugins/PluginDebugAndroid.cpp @@ -60,7 +60,7 @@ void anp_logPlugin(const char format[], ...) { va_end(args); } -void anp_logPluginEvent(void* npp, const ANPEvent* evt, int16 returnVal, int elapsedTime) { +void anp_logPluginEvent(void* npp, const ANPEvent* evt, int16_t returnVal, int elapsedTime) { switch(evt->eventType) { diff --git a/WebKit/android/plugins/PluginTimer.cpp b/WebKit/android/plugins/PluginTimer.cpp index a813d25..ae7cb84 100644 --- a/WebKit/android/plugins/PluginTimer.cpp +++ b/WebKit/android/plugins/PluginTimer.cpp @@ -29,10 +29,10 @@ namespace WebCore { - static uint32 gTimerID; + static uint32_t gTimerID; PluginTimer::PluginTimer(PluginTimer** list, NPP instance, bool repeat, - void (*timerFunc)(NPP npp, uint32 timerID)) + void (*timerFunc)(NPP npp, uint32_t timerID)) : m_list(list), m_instance(instance), m_timerFunc(timerFunc), @@ -71,7 +71,7 @@ namespace WebCore { } // may return null if timerID is not found - PluginTimer* PluginTimer::Find(PluginTimer* list, uint32 timerID) + PluginTimer* PluginTimer::Find(PluginTimer* list, uint32_t timerID) { PluginTimer* curr = list; while (curr) { @@ -92,8 +92,8 @@ namespace WebCore { } } - uint32 PluginTimerList::schedule(NPP instance, uint32 interval, bool repeat, - void (*proc)(NPP npp, uint32 timerID)) + uint32_t PluginTimerList::schedule(NPP instance, uint32_t interval, bool repeat, + void (*proc)(NPP npp, uint32_t timerID)) { PluginTimer* timer = new PluginTimer(&m_list, instance, repeat, proc); @@ -106,7 +106,7 @@ namespace WebCore { return timer->timerID(); } - void PluginTimerList::unschedule(NPP instance, uint32 timerID) + void PluginTimerList::unschedule(NPP instance, uint32_t timerID) { // Although it looks like simply deleting the timer would work here // (stop() will be executed by the dtor), we cannot do this, as diff --git a/WebKit/android/plugins/PluginTimer.h b/WebKit/android/plugins/PluginTimer.h index 2ffe437..dcb29bf 100644 --- a/WebKit/android/plugins/PluginTimer.h +++ b/WebKit/android/plugins/PluginTimer.h @@ -37,14 +37,14 @@ namespace WebCore { class PluginTimer : public TimerBase { public: PluginTimer(PluginTimer** list, NPP instance, bool repeat, - void (*proc)(NPP npp, uint32 timerID)); + void (*proc)(NPP npp, uint32_t timerID)); virtual ~PluginTimer(); - uint32 timerID() const { return m_timerID; } + uint32_t timerID() const { return m_timerID; } void unschedule() { m_unscheduled = true; } - static PluginTimer* Find(PluginTimer* list, uint32 timerID); + static PluginTimer* Find(PluginTimer* list, uint32_t timerID); private: // override from TimerBase @@ -57,8 +57,8 @@ namespace WebCore { PluginTimer* m_prev; PluginTimer* m_next; NPP m_instance; - void (*m_timerFunc)(NPP, uint32); - uint32 m_timerID; + void (*m_timerFunc)(NPP, uint32_t); + uint32_t m_timerID; bool m_repeat; bool m_unscheduled; }; @@ -68,9 +68,9 @@ namespace WebCore { PluginTimerList() : m_list(0) {} ~PluginTimerList(); - uint32 schedule(NPP instance, uint32 interval, bool repeat, - void (*proc)(NPP npp, uint32 timerID)); - void unschedule(NPP instance, uint32 timerID); + uint32_t schedule(NPP instance, uint32_t interval, bool repeat, + void (*proc)(NPP npp, uint32_t timerID)); + void unschedule(NPP instance, uint32_t timerID); private: PluginTimer* m_list; diff --git a/WebKit/android/plugins/PluginWidgetAndroid.cpp b/WebKit/android/plugins/PluginWidgetAndroid.cpp index c5a1f5b..b90b53b 100644 --- a/WebKit/android/plugins/PluginWidgetAndroid.cpp +++ b/WebKit/android/plugins/PluginWidgetAndroid.cpp @@ -275,7 +275,7 @@ void PluginWidgetAndroid::layoutSurface(bool pluginBoundsChanged) { } } -int16 PluginWidgetAndroid::sendEvent(const ANPEvent& evt) { +int16_t PluginWidgetAndroid::sendEvent(const ANPEvent& evt) { if (!m_acceptEvents) return 0; WebCore::PluginPackage* pkg = m_pluginView->plugin(); @@ -297,7 +297,7 @@ int16 PluginWidgetAndroid::sendEvent(const ANPEvent& evt) { // make a localCopy since the actual plugin may not respect its constness, // and so we don't want our caller to have its param modified ANPEvent localCopy = evt; - int16 result = pkg->pluginFuncs()->event(instance, &localCopy); + int16_t result = pkg->pluginFuncs()->event(instance, &localCopy); #if DEBUG_EVENTS SkMSec endTime = SkTime::GetMSecs(); diff --git a/WebKit/android/plugins/PluginWidgetAndroid.h b/WebKit/android/plugins/PluginWidgetAndroid.h index 2e55aaa..162fb1d 100644 --- a/WebKit/android/plugins/PluginWidgetAndroid.h +++ b/WebKit/android/plugins/PluginWidgetAndroid.h @@ -97,7 +97,7 @@ struct PluginWidgetAndroid { /* Send this event to the plugin instance. A non-zero value will be returned if the plugin handled the event. */ - int16 sendEvent(const ANPEvent&); + int16_t sendEvent(const ANPEvent&); /* Update the plugins event flags. If a flag is set to true then the plugin wants to be notified of events of this type. diff --git a/WebKit/android/smoke/MessageThread.cpp b/WebKit/android/smoke/MessageThread.cpp new file mode 100644 index 0000000..48f2222 --- /dev/null +++ b/WebKit/android/smoke/MessageThread.cpp @@ -0,0 +1,146 @@ +/* + * Copyright 2010, 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 "MessageThread" + +#include "config.h" + +#include <sys/time.h> +#include <time.h> + +#include "MessageThread.h" +#include "ScriptController.h" + +#include <utils/Log.h> + +namespace android { + +static bool compareMessages(const Message& msg1, + const Message& msg2, + bool memberIsNull) { + return (msg1.object() == msg2.object() && + (memberIsNull || msg1.member() == msg2.member())); +} + +bool MessageQueue::hasMessages(const Message& message) { + AutoMutex lock(m_mutex); + + static const Message::GenericMemberFunction nullMember = NULL; + const bool memberIsNull = message.member() == nullMember; + + for (list<Message*>::iterator it = m_messages.begin(); + it != m_messages.end(); ++it) { + Message* m = *it; + if (compareMessages(message, *m, memberIsNull)) + return true; + } + return false; +} + +void MessageQueue::remove(const Message& message) { + AutoMutex lock(m_mutex); + + static const Message::GenericMemberFunction nullMember = NULL; + const bool memberIsNull = message.member() == nullMember; + + for (list<Message*>::iterator it = m_messages.begin(); + it != m_messages.end(); ++it) { + Message* m = *it; + if (compareMessages(message, *m, memberIsNull)) { + it = m_messages.erase(it); + delete m; + } + } +} + +void MessageQueue::post(Message* message) { + AutoMutex lock(m_mutex); + + double when = message->m_when; + LOG_ASSERT(when > 0, "Message time may not be 0"); + + list<Message*>::iterator it; + for (it = m_messages.begin(); it != m_messages.end(); ++it) { + Message* m = *it; + if (when < m->m_when) { + break; + } + } + m_messages.insert(it, message); + m_condition.signal(); +} + +void MessageQueue::postAtFront(Message* message) { + AutoMutex lock(m_mutex); + message->m_when = 0; + m_messages.push_front(message); +} + +Message* MessageQueue::next() { + AutoMutex lock(m_mutex); + while (true) { + if (m_messages.empty()) { + // No messages, wait until another arrives + m_condition.wait(m_mutex); + } + Message* next = m_messages.front(); + double now = WTF::currentTimeMS(); + double diff = next->m_when - now; + if (diff > 0) { + // Not time for this message yet, wait the difference in nanos + m_condition.waitRelative(m_mutex, + static_cast<nsecs_t>(diff * 1000000) /* nanos */); + } else { + // Time for this message to run. + m_messages.pop_front(); + return next; + } + } +} + +bool MessageThread::threadLoop() { + WebCore::ScriptController::initializeThreading(); + + while (true) { + Message* message = m_queue.next(); + if (message != NULL) { + message->run(); + } + } + return false; +} + +// Global thread object obtained by messageThread(). +static sp<MessageThread> gMessageThread; + +MessageThread* messageThread() { + if (gMessageThread == NULL) { + gMessageThread = new MessageThread(); + gMessageThread->run("WebCoreThread"); + } + return gMessageThread.get(); +} + +} // namespace android diff --git a/WebKit/android/smoke/MessageThread.h b/WebKit/android/smoke/MessageThread.h new file mode 100644 index 0000000..ca0115b --- /dev/null +++ b/WebKit/android/smoke/MessageThread.h @@ -0,0 +1,108 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef ANDROID_WEBKIT_MESSAGETHREAD_H +#define ANDROID_WEBKIT_MESSAGETHREAD_H + +#include <list> + +#include "MessageTypes.h" + +#include <utils/threads.h> + +using std::list; + +namespace android { + +class MessageQueue { +public: + MessageQueue() {} + + // Return true if the queue has messages with the given object and member + // function. If member is null, return true if the message has the same + // object. + template <class T> + bool hasMessages(T* object, void (T::*member)(void)); + + // Remove all messages with the given object and member function. If + // member is null, remove all messages with the given object. + template <class T> + void remove(T* object, void (T::*member)(void)); + + // Post a new message to the queue. + void post(Message* closure); + + // Post a new message at the front of the queue. + void postAtFront(Message* closure); + + // Obtain the next message. Blocks until either a new message arrives or + // we reach the time of the next message. + Message* next(); + +private: + bool hasMessages(const Message& message); + void remove(const Message& message); + + list<Message*> m_messages; + Mutex m_mutex; + Condition m_condition; +}; + +template <class T> +bool MessageQueue::hasMessages(T* object, void (T::*member)(void)) { + MemberFunctionMessage<T, void> message(object, member); + return hasMessages(message); +} + +template <class T> +void MessageQueue::remove(T* object, void (T::*member)(void)) { + MemberFunctionMessage<T, void> message(object, member); + remove(message); +} + +class MessageThread : public Thread { +public: + MessageQueue& queue() { return m_queue; } + +private: + MessageThread() : Thread(true /* canCallJava */) {} + + virtual bool threadLoop(); + + MessageQueue m_queue; + // Used for thread initialization + Mutex m_mutex; + Condition m_condition; + + friend MessageThread* messageThread(); +}; + +// Get (possibly creating) the global MessageThread object used to pass +// messages to WebCore. +MessageThread* messageThread(); + +} // namespace android + +#endif // ANDROID_WEBKIT_MESSAGETHREAD_H diff --git a/WebKit/android/smoke/MessageTypes.h b/WebKit/android/smoke/MessageTypes.h new file mode 100644 index 0000000..7da6cb8 --- /dev/null +++ b/WebKit/android/smoke/MessageTypes.h @@ -0,0 +1,159 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef ANDROID_WEBKIT_MESSAGETYPES_H_ +#define ANDROID_WEBKIT_MESSAGETYPES_H_ + +#include <wtf/CurrentTime.h> + +// TODO(phanna): autogenerate these types! + +namespace android { + +// Forward declared for friendship! +class MessageQueue; + +// Removes the reference from the typename so we store the actual value in the +// closure. +template <typename T> struct remove_reference { typedef T type; }; +template <typename T> struct remove_reference<T&> { typedef T type; }; + +// Prevent the compiler from inferring the type. +template <typename T> struct identity { typedef T type; }; + +// Message base class. Defines the public run() method and contains generic +// object and member function variables for use in MessageQueue. +// +// Note: The template subclass MemberFunctionMessage casts its object and +// member function to the generic void* and Message::* types. During run(), +// each template specialization downcasts to the original type and invokes the +// correct function. This may seem dangerous but the compiler enforces +// correctness in NewMessage and in the template constructor. +class Message { +public: + typedef void (Message::*GenericMemberFunction)(void); + + virtual ~Message() {} + virtual void run() = 0; + + // The wall time that the message is supposed to run. + double m_when; + + void* object() const { return m_object; } + GenericMemberFunction member() const { return m_member; } + +protected: + Message(void* object, GenericMemberFunction member, long delay = 0) + : m_object(object) + , m_member(member) { + m_when = WTF::currentTimeMS() + delay; + } + + // Downcast back to the original template params in run(). Also accessed + // by MessageQueue to compare messages. + void* m_object; + GenericMemberFunction m_member; + +private: + // Disallow copy + Message(const Message&); +}; + +// Forward declaration for partial specialization. +template <class T, typename A1> +class MemberFunctionMessage; + +template <class T> +class MemberFunctionMessage<T, void> : public Message { +private: + typedef void (T::*MemberSignature)(); + +public: + inline MemberFunctionMessage(T* object, + MemberSignature member, + long delay = 0) + : Message(reinterpret_cast<void*>(object), + reinterpret_cast<GenericMemberFunction>(member), + delay) {} + + virtual void run() { + MemberSignature member = reinterpret_cast<MemberSignature>(m_member); + (reinterpret_cast<T*>(m_object)->*member)(); + delete this; + } +}; + +template <class T> +inline Message* NewMessage(T* object, void (T::*member)()) { + return new MemberFunctionMessage<T, void>(object, member); +} + +template <class T> +inline Message* NewDelayedMessage(T* object, void (T::*member)(), long delay) { + return new MemberFunctionMessage<T, void>(object, member, delay); +} + +template <class T, typename A1> +class MemberFunctionMessage : public Message { +private: + typedef void (T::*MemberSignature)(A1); + +public: + inline MemberFunctionMessage(T* object, + MemberSignature member, + A1 arg1, + long delay = 0) + : Message(reinterpret_cast<void*>(object), + reinterpret_cast<GenericMemberFunction>(member), + delay) + , m_arg1(arg1) {} + + virtual void run() { + MemberSignature member = reinterpret_cast<MemberSignature>(m_member); + (reinterpret_cast<T*>(m_object)->*member)(m_arg1); + delete this; + } + +private: + typename remove_reference<A1>::type m_arg1; +}; + +template <class T, typename A1> +inline Message* NewMessage(T* object, void (T::*member)(A1), + typename identity<A1>::type arg1) { + return new MemberFunctionMessage<T, A1>( + object, member, arg1); +} + +template <class T, typename A1> +inline Message* NewDelayedMessage(T* object, void (T::*member)(A1), + typename identity<A1>::type arg1, long delay) { + return new MemberFunctionMessage<T, A1>(object, member, arg1, delay); +} + +} // namespace android + + +#endif // ANDROID_WEBKIT_MESSAGETYPES_H_ diff --git a/WebKit/android/wds/Command.cpp b/WebKit/android/wds/Command.cpp index 7f2046e..bd8536f 100644 --- a/WebKit/android/wds/Command.cpp +++ b/WebKit/android/wds/Command.cpp @@ -27,7 +27,6 @@ #include "config.h" #include "AndroidLog.h" -#include "CString.h" #include "Command.h" #include "Connection.h" #include "DebugServer.h" @@ -36,6 +35,7 @@ #include "RenderView.h" #include "WebViewCore.h" #include <utils/Log.h> +#include <wtf/text/CString.h> #if ENABLE(WDS) |
