From 8216a0e796895ec6e736aebbeacee9567ae85515 Mon Sep 17 00:00:00 2001 From: Patrick Scott Date: Mon, 22 Mar 2010 10:09:16 -0400 Subject: Add on-demand plugin support. The Settings object now has an on-demand flag for plugins (this was to avoid more edits to webkit code). If plugins are on-demand and a plugin is installed that can handle the content, insert a placeholder widget. If the user clicks on the placeholder, the plugin will be enabled. The widget currently does not clip the context correctly. It only clips based on the widget frame. This is due to a bug (already filed) where the scroll offset is producing bad clip rectangles. Requires a framework change. Bug: 2411524 Change-Id: If3931da8da2339a2385ae78b609c49fa069892ab --- WebCore/page/Settings.cpp | 3 + WebCore/page/Settings.h | 8 ++ WebCore/platform/android/TemporaryLinkStubs.cpp | 5 + WebCore/platform/android/WidgetAndroid.cpp | 5 +- .../WebCoreSupport/FrameLoaderClientAndroid.cpp | 137 +++++++++++++++++++-- WebKit/android/jni/WebSettings.cpp | 18 ++- WebKit/android/nav/CacheBuilder.cpp | 3 +- 7 files changed, 156 insertions(+), 23 deletions(-) diff --git a/WebCore/page/Settings.cpp b/WebCore/page/Settings.cpp index 7b5da91..c495a23 100644 --- a/WebCore/page/Settings.cpp +++ b/WebCore/page/Settings.cpp @@ -136,6 +136,9 @@ Settings::Settings(Page* page) , m_webGLEnabled(false) , m_geolocationEnabled(true) , m_loadDeferringEnabled(true) +#ifdef ANDROID_PLUGINS + , m_pluginsOnDemand(false) +#endif { // A Frame may not have been created yet, so we initialize the AtomicString // hash before trying to use it. diff --git a/WebCore/page/Settings.h b/WebCore/page/Settings.h index fc99ac8..652c13d 100644 --- a/WebCore/page/Settings.h +++ b/WebCore/page/Settings.h @@ -147,6 +147,11 @@ namespace WebCore { void setPluginsEnabled(bool); bool arePluginsEnabled() const { return m_arePluginsEnabled; } +#ifdef ANDROID_PLUGINS + void setPluginsOnDemand(bool onDemand) { m_pluginsOnDemand = onDemand; } + bool arePluginsOnDemand() const { return m_pluginsOnDemand; } +#endif + void setDatabasesEnabled(bool); bool databasesEnabled() const { return m_databasesEnabled; } @@ -458,6 +463,9 @@ namespace WebCore { bool m_webGLEnabled : 1; bool m_geolocationEnabled : 1; bool m_loadDeferringEnabled : 1; +#ifdef ANDROID_PLUGINS + bool m_pluginsOnDemand : 1; +#endif #if USE(SAFARI_THEME) static bool gShouldPaintNativeControls; diff --git a/WebCore/platform/android/TemporaryLinkStubs.cpp b/WebCore/platform/android/TemporaryLinkStubs.cpp index 9741ad5..6dac5ef 100644 --- a/WebCore/platform/android/TemporaryLinkStubs.cpp +++ b/WebCore/platform/android/TemporaryLinkStubs.cpp @@ -67,6 +67,7 @@ #include "Pasteboard.h" #include "Path.h" #include "PluginInfoStore.h" +#include "PluginWidget.h" #include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceLoader.h" @@ -144,6 +145,10 @@ void refreshPlugins(bool) #endif // !defined(ANDROID_PLUGINS) +// Needed to link with PluginWidget as a parent class of PluginToggleWidget. Mac +// defines this in plugins/mac/PluginWidgetMac.mm +void PluginWidget::invalidateRect(const IntRect&) {} + // This function tells the bridge that a resource was loaded from the cache and thus // the app may update progress with the amount of data loaded. void CheckCacheObjectStatus(DocLoader*, CachedResource*) diff --git a/WebCore/platform/android/WidgetAndroid.cpp b/WebCore/platform/android/WidgetAndroid.cpp index d122ef7..9ab0b2c 100644 --- a/WebCore/platform/android/WidgetAndroid.cpp +++ b/WebCore/platform/android/WidgetAndroid.cpp @@ -49,9 +49,8 @@ Widget::~Widget() IntRect Widget::frameRect() const { - // FIXME: use m_frame instead? if (!platformWidget()) - return IntRect(0, 0, 0, 0); + return m_frame; return platformWidget()->getBounds(); } @@ -95,7 +94,7 @@ void Widget::hide() void Widget::setFrameRect(const IntRect& rect) { - // FIXME: set m_frame instead? + m_frame = rect; // platformWidget() is 0 when called from Scrollbar if (!platformWidget()) return; diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp index 6e42d47..5fb7d29 100644 --- a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp @@ -53,6 +53,7 @@ #include "PlatformString.h" #include "PluginDatabase.h" #include "PluginView.h" +#include "PluginWidget.h" #include "ProgressTracker.h" #include "RenderPart.h" #include "ResourceError.h" @@ -1000,6 +1001,93 @@ static bool isYouTubeInstalled() { return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube"); } +// Use PluginWidget as it is not used by Android for real plugins. +class PluginToggleWidget : public PluginWidget { +public: + PluginToggleWidget(Frame* parent, const IntSize& size, + HTMLPlugInElement* elem, const KURL& url, + const WTF::Vector& paramNames, + const WTF::Vector& paramValues, const String& mimeType, + bool loadManually) + : m_parent(parent) + , m_size(size) + , m_element(elem) + , m_url(url) + , m_paramNames(paramNames) + , m_paramValues(paramValues) + , m_mimeType(mimeType) + , m_loadManually(loadManually) + { + resize(size); + } + + virtual void paint(GraphicsContext* ctx, const IntRect& rect) + { + // Most of this code is copied from PluginView::paintMissingPluginIcon + // with slight modification. + + static RefPtr image; + static RefPtr bg; + if (!image || !bg) { + image = Image::loadPlatformResource("togglePlugin"); + bg = Image::loadPlatformResource("togglePluginBg"); + } + + IntRect imageRect(x(), y(), image->width(), image->height()); + + int xOffset = (width() - imageRect.width()) >> 1; + int yOffset = (height() - imageRect.height()) >> 1; + + imageRect.move(xOffset, yOffset); + + if (!rect.intersects(imageRect)) + return; + + // FIXME: We need to clip similarly to paintMissingPluginIcon but it is + // way screwed up right now. It has something to do with how we tell + // webkit the scroll position and it causes the placeholder to get + // clipped very badly. http://b/issue?id=2533303 + + ctx->save(); + ctx->clip(frameRect()); + ctx->drawTiledImage(bg.get(), DeviceColorSpace, frameRect(), + IntPoint(), IntSize(bg->width(), bg->height())); + ctx->drawImage(image.get(), DeviceColorSpace, imageRect.location()); + ctx->restore(); + } + + virtual void handleEvent(Event* event) + { + if (event->type() != eventNames().clickEvent) + return; + + WTF::PassRefPtr prpWidget = + PluginView::create(m_parent.get(), + m_size, + m_element, + m_url, + m_paramNames, + m_paramValues, + m_mimeType, + m_loadManually); + RefPtr myProtector(this); + RenderWidget* renderer = + static_cast(m_element->renderer()); + prpWidget->focusPluginElement(); + renderer->setWidget(prpWidget); + } + +private: + RefPtr m_parent; + IntSize m_size; + HTMLPlugInElement* m_element; + KURL m_url; + WTF::Vector m_paramNames; + WTF::Vector m_paramValues; + String m_mimeType; + bool m_loadManually; +}; + WTF::PassRefPtr FrameLoaderClientAndroid::createPlugin( const IntSize& size, HTMLPlugInElement* element, @@ -1008,6 +1096,41 @@ WTF::PassRefPtr FrameLoaderClientAndroid::createPlugin( const WTF::Vector& values, const String& mimeType, bool loadManually) { + WTF::PassRefPtr prpWidget = 0; +#ifdef ANDROID_PLUGINS + // This is copied from PluginView.cpp. We need to determine if a plugin + // will be found before doing some of the work in PluginView. + String mimeTypeCopy = mimeType; + PluginPackage* plugin = + PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); + if (!plugin && PluginDatabase::installedPlugins()->refresh()) { + mimeTypeCopy = mimeType; + plugin = PluginDatabase::installedPlugins()->findPlugin(url, + mimeTypeCopy); + } + Settings* settings = m_frame->settings(); + // Do the placeholder if plugins are on-demand and there is a plugin for the + // given mime type. + if (settings && settings->arePluginsOnDemand() && plugin) { + return adoptRef(new PluginToggleWidget(m_frame, size, element, url, + names, values, mimeType, loadManually)); + } + prpWidget = PluginView::create(m_frame, + size, + element, + url, + names, + values, + mimeType, + loadManually); + // Return the plugin if it was loaded successfully. Otherwise, fallback to + // the youtube placeholder if possible. No need to check prpWidget as + // PluginView::create will create a PluginView for missing plugins. + // Note: this check really only checks if the plugin was found and not if + // the plugin was loaded. + if (prpWidget->status() == PluginStatusLoadedSuccessfully) + return prpWidget; +#endif // Create an iframe for youtube urls. if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) { WTF::RefPtr frame = createFrame(blankURL(), String(), element, @@ -1037,20 +1160,8 @@ WTF::PassRefPtr FrameLoaderClientAndroid::createPlugin( WTF::RefPtr widget(frame->view()); return widget.release(); } - return NULL; } -#ifdef ANDROID_PLUGINS - return PluginView::create(m_frame, - size, - element, - url, - names, - values, - mimeType, - loadManually); -#else - return NULL; -#endif + return prpWidget; } void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) { diff --git a/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp index 91ed8cc..70ecded 100644 --- a/WebKit/android/jni/WebSettings.cpp +++ b/WebKit/android/jni/WebSettings.cpp @@ -83,7 +83,8 @@ struct FieldIds { mBlockNetworkImage = env->GetFieldID(clazz, "mBlockNetworkImage", "Z"); #endif mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z"); - mPluginsEnabled = env->GetFieldID(clazz, "mPluginsEnabled", "Z"); + mPluginState = env->GetFieldID(clazz, "mPluginState", + "Landroid/webkit/WebSettings$PluginState;"); #if ENABLE(DATABASE) mDatabaseEnabled = env->GetFieldID(clazz, "mDatabaseEnabled", "Z"); #endif @@ -133,7 +134,7 @@ struct FieldIds { LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage"); #endif LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled"); - LOG_ASSERT(mPluginsEnabled, "Could not find field mPluginsEnabled"); + LOG_ASSERT(mPluginState, "Could not find field mPluginState"); #if ENABLE(OFFLINE_WEB_APPLICATIONS) LOG_ASSERT(mAppCacheEnabled, "Could not find field mAppCacheEnabled"); LOG_ASSERT(mAppCachePath, "Could not find field mAppCachePath"); @@ -179,7 +180,7 @@ struct FieldIds { jfieldID mBlockNetworkImage; #endif jfieldID mJavaScriptEnabled; - jfieldID mPluginsEnabled; + jfieldID mPluginState; #if ENABLE(OFFLINE_WEB_APPLICATIONS) jfieldID mAppCacheEnabled; jfieldID mAppCachePath; @@ -313,8 +314,15 @@ public: flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled); s->setJavaScriptEnabled(flag); - flag = env->GetBooleanField(obj, gFieldIds->mPluginsEnabled); - s->setPluginsEnabled(flag); + // ON = 0 + // ON_DEMAND = 1 + // OFF = 2 + jobject pluginState = env->GetObjectField(obj, gFieldIds->mPluginState); + int state = env->CallIntMethod(pluginState, gFieldIds->mOrdinal); + s->setPluginsEnabled(state < 2); +#ifdef ANDROID_PLUGINS + s->setPluginsOnDemand(state == 1); +#endif #if ENABLE(OFFLINE_WEB_APPLICATIONS) flag = env->GetBooleanField(obj, gFieldIds->mAppCacheEnabled); diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp index d1d36df..a3aeede 100644 --- a/WebKit/android/nav/CacheBuilder.cpp +++ b/WebKit/android/nav/CacheBuilder.cpp @@ -878,8 +878,7 @@ static Node* OneAfter(Node* node) static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) { if (renderer->isWidget()) { Widget* widget = static_cast(renderer)->widget(); - if (widget && widget->isPluginView()) { - PluginView* pv = static_cast(widget); + if (widget && (widget->isPluginView() || widget->isPluginWidget())) { // check if this plugin really wants key events (TODO) return true; } -- cgit v1.1