diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk')
34 files changed, 4053 insertions, 469 deletions
diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.cpp b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.cpp new file mode 100644 index 000000000..7618dffe0 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "AcceleratedBackingStore.h" + +#include "WebPageProxy.h" +#include <WebCore/CairoUtilities.h> +#include <WebCore/PlatformDisplay.h> + +#if PLATFORM(WAYLAND) +#include "AcceleratedBackingStoreWayland.h" +#endif + +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) +#include "AcceleratedBackingStoreX11.h" +#endif + +using namespace WebCore; + +namespace WebKit { + +std::unique_ptr<AcceleratedBackingStore> AcceleratedBackingStore::create(WebPageProxy& webPage) +{ +#if PLATFORM(WAYLAND) && USE(EGL) + if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland) + return AcceleratedBackingStoreWayland::create(webPage); +#endif +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) + if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::X11) + return AcceleratedBackingStoreX11::create(webPage); +#endif + return nullptr; +} + +AcceleratedBackingStore::AcceleratedBackingStore(WebPageProxy& webPage) + : m_webPage(webPage) +{ +} + +bool AcceleratedBackingStore::paint(cairo_t* cr, const IntRect& clipRect) +{ + if (m_webPage.drawsBackground()) + return true; + + const WebCore::Color& color = m_webPage.backgroundColor(); + if (!color.isOpaque()) { + cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_fill(cr); + } + + if (color.isVisible()) { + setSourceRGBAFromColor(cr, color); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + cairo_fill(cr); + } + + return true; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.h b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.h new file mode 100644 index 000000000..3de3b5dfa --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#include <wtf/Noncopyable.h> + +typedef struct _cairo cairo_t; + +namespace WebCore { +class IntRect; +} + +namespace WebKit { + +class LayerTreeContext; +class WebPageProxy; + +class AcceleratedBackingStore { + WTF_MAKE_NONCOPYABLE(AcceleratedBackingStore); WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<AcceleratedBackingStore> create(WebPageProxy&); + virtual ~AcceleratedBackingStore() = default; + + virtual void update(const LayerTreeContext&) { } + virtual bool paint(cairo_t*, const WebCore::IntRect&); + +protected: + AcceleratedBackingStore(WebPageProxy&); + + WebPageProxy& m_webPage; +}; + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.cpp b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.cpp new file mode 100644 index 000000000..12e03c43f --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "AcceleratedBackingStoreWayland.h" + +#if PLATFORM(WAYLAND) && USE(EGL) + +#include "WaylandCompositor.h" +#include "WebPageProxy.h" +#include <WebCore/CairoUtilities.h> +#include <WebCore/RefPtrCairo.h> + +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#else +#include <WebCore/OpenGLShims.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +std::unique_ptr<AcceleratedBackingStoreWayland> AcceleratedBackingStoreWayland::create(WebPageProxy& webPage) +{ + if (!WaylandCompositor::singleton().isRunning()) + return nullptr; + return std::unique_ptr<AcceleratedBackingStoreWayland>(new AcceleratedBackingStoreWayland(webPage)); +} + +AcceleratedBackingStoreWayland::AcceleratedBackingStoreWayland(WebPageProxy& webPage) + : AcceleratedBackingStore(webPage) +{ + WaylandCompositor::singleton().registerWebPage(m_webPage); +} + +AcceleratedBackingStoreWayland::~AcceleratedBackingStoreWayland() +{ + WaylandCompositor::singleton().unregisterWebPage(m_webPage); +} + +#if GTK_CHECK_VERSION(3, 16, 0) +bool AcceleratedBackingStoreWayland::canGdkUseGL() const +{ + static bool initialized = false; + static bool canCreateGLContext = false; + + if (initialized) + return canCreateGLContext; + + initialized = true; + + GUniqueOutPtr<GError> error; + GdkWindow* gdkWindow = gtk_widget_get_window(m_webPage.viewWidget()); + GRefPtr<GdkGLContext> gdkContext(gdk_window_create_gl_context(gdkWindow, &error.outPtr())); + if (!gdkContext) { + g_warning("GDK is not able to create a GL context, falling back to glReadPixels (slow!): %s", error->message); + return false; + } + + canCreateGLContext = true; + + return true; +} +#endif + +bool AcceleratedBackingStoreWayland::paint(cairo_t* cr, const IntRect& clipRect) +{ + GLuint texture; + IntSize textureSize; + if (!WaylandCompositor::singleton().getTexture(m_webPage, texture, textureSize)) + return false; + + cairo_save(cr); + AcceleratedBackingStore::paint(cr, clipRect); + +#if GTK_CHECK_VERSION(3, 16, 0) + if (canGdkUseGL()) { + gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(m_webPage.viewWidget()), texture, GL_TEXTURE, m_webPage.deviceScaleFactor(), 0, 0, textureSize.width(), textureSize.height()); + cairo_restore(cr); + return true; + } +#endif + + if (!m_surface || cairo_image_surface_get_width(m_surface.get()) != textureSize.width() || cairo_image_surface_get_height(m_surface.get()) != textureSize.height()) + m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, textureSize.width(), textureSize.height())); + + cairoSurfaceSetDeviceScale(m_surface.get(), m_webPage.deviceScaleFactor(), m_webPage.deviceScaleFactor()); + + GLuint fb; + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + +#if USE(OPENGL_ES_2) + unsigned char* data = cairo_image_surface_get_data(m_surface.get()); + if (cairo_image_surface_get_stride(m_surface.get()) == textureSize.width() * 4) + glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, data); + else { + int strideBytes = cairo_image_surface_get_stride(m_surface.get()); + for (int i = 0; i < textureSize.height(); i++) { + unsigned char* dataOffset = data + i * strideBytes; + glReadPixels(0, i, textureSize.width(), 1, GL_RGBA, GL_UNSIGNED_BYTE, dataOffset); + } + } + + // Convert to BGRA. + int totalBytes = textureSize.width() * textureSize.height() * 4; + for (int i = 0; i < totalBytes; i += 4) + std::swap(data[i], data[i + 2]); +#else + glPixelStorei(GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride(m_surface.get()) / 4); + glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(m_surface.get())); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fb); + + // The surface can be modified by the web process at any time, so we mark it + // as dirty to ensure we always render the updated contents as soon as possible. + cairo_surface_mark_dirty(m_surface.get()); + + // The compositor renders the texture flipped for gdk_cairo_draw_from_gl, fix that here. + cairo_matrix_t transform; + cairo_matrix_init(&transform, 1, 0, 0, -1, 0, textureSize.height() / m_webPage.deviceScaleFactor()); + cairo_transform(cr, &transform); + + cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + cairo_set_source_surface(cr, m_surface.get(), 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_fill(cr); + + cairo_restore(cr); + + return true; +} + +} // namespace WebKit + +#endif // PLATFORM(WAYLAND) && USE(EGL) diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.h b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.h new file mode 100644 index 000000000..de6048aef --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#include "AcceleratedBackingStore.h" + +#if PLATFORM(WAYLAND) + +#include <WebCore/RefPtrCairo.h> +#include <gtk/gtk.h> + +namespace WebKit { + +class WebPageProxy; + +class AcceleratedBackingStoreWayland final : public AcceleratedBackingStore { + WTF_MAKE_NONCOPYABLE(AcceleratedBackingStoreWayland); WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<AcceleratedBackingStoreWayland> create(WebPageProxy&); + ~AcceleratedBackingStoreWayland(); + +#if GTK_CHECK_VERSION(3, 16, 0) + bool canGdkUseGL() const; +#endif + +private: + AcceleratedBackingStoreWayland(WebPageProxy&); + + bool paint(cairo_t*, const WebCore::IntRect&) override; + + RefPtr<cairo_surface_t> m_surface; +}; + +} // namespace WebKit + +#endif // PLATFORM(WAYLAND) diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.cpp b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.cpp new file mode 100644 index 000000000..d24a0238f --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "AcceleratedBackingStoreX11.h" + +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) + +#include "DrawingAreaProxyImpl.h" +#include "LayerTreeContext.h" +#include "WebPageProxy.h" +#include <WebCore/CairoUtilities.h> +#include <WebCore/PlatformDisplayX11.h> +#include <WebCore/XErrorTrapper.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xdamage.h> +#include <cairo-xlib.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> + +using namespace WebCore; + +namespace WebKit { + +static std::optional<int> s_damageEventBase; +static std::optional<int> s_damageErrorBase; + +class XDamageNotifier { + WTF_MAKE_NONCOPYABLE(XDamageNotifier); + friend class NeverDestroyed<XDamageNotifier>; +public: + static XDamageNotifier& singleton() + { + static NeverDestroyed<XDamageNotifier> notifier; + return notifier; + } + + void add(Damage damage, std::function<void()>&& notifyFunction) + { + if (m_notifyFunctions.isEmpty()) + gdk_window_add_filter(nullptr, reinterpret_cast<GdkFilterFunc>(&filterXDamageEvent), this); + m_notifyFunctions.add(damage, WTFMove(notifyFunction)); + } + + void remove(Damage damage) + { + m_notifyFunctions.remove(damage); + if (m_notifyFunctions.isEmpty()) + gdk_window_remove_filter(nullptr, reinterpret_cast<GdkFilterFunc>(&filterXDamageEvent), this); + } + +private: + XDamageNotifier() = default; + + static GdkFilterReturn filterXDamageEvent(GdkXEvent* event, GdkEvent*, XDamageNotifier* notifier) + { + auto* xEvent = static_cast<XEvent*>(event); + if (xEvent->type != s_damageEventBase.value() + XDamageNotify) + return GDK_FILTER_CONTINUE; + + auto* damageEvent = reinterpret_cast<XDamageNotifyEvent*>(xEvent); + if (notifier->notify(damageEvent->damage)) { + XDamageSubtract(xEvent->xany.display, damageEvent->damage, None, None); + return GDK_FILTER_REMOVE; + } + + return GDK_FILTER_CONTINUE; + } + + bool notify(Damage damage) const + { + if (const auto& notifyFunction = m_notifyFunctions.get(damage)) { + notifyFunction(); + return true; + } + return false; + } + + HashMap<Damage, std::function<void()>> m_notifyFunctions; +}; + +std::unique_ptr<AcceleratedBackingStoreX11> AcceleratedBackingStoreX11::create(WebPageProxy& webPage) +{ + auto& display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()); + if (!display.supportsXComposite() || !display.supportsXDamage(s_damageEventBase, s_damageErrorBase)) + return nullptr; + return std::unique_ptr<AcceleratedBackingStoreX11>(new AcceleratedBackingStoreX11(webPage)); +} + +AcceleratedBackingStoreX11::AcceleratedBackingStoreX11(WebPageProxy& webPage) + : AcceleratedBackingStore(webPage) +{ +} + +static inline unsigned char xDamageErrorCode(unsigned char errorCode) +{ + ASSERT(s_damageErrorBase); + return static_cast<unsigned>(s_damageErrorBase.value()) + errorCode; +} + +AcceleratedBackingStoreX11::~AcceleratedBackingStoreX11() +{ + if (!m_surface && !m_damage) + return; + + Display* display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(); + XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); + if (m_damage) { + XDamageNotifier::singleton().remove(m_damage.get()); + m_damage.reset(); + XSync(display, False); + } +} + +void AcceleratedBackingStoreX11::update(const LayerTreeContext& layerTreeContext) +{ + Pixmap pixmap = layerTreeContext.contextID; + if (m_surface && cairo_xlib_surface_get_drawable(m_surface.get()) == pixmap) + return; + + Display* display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(); + + if (m_surface) { + XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); + if (m_damage) { + XDamageNotifier::singleton().remove(m_damage.get()); + m_damage.reset(); + XSync(display, False); + } + m_surface = nullptr; + } + + if (!pixmap) + return; + + DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(m_webPage.drawingArea()); + if (!drawingArea) + return; + + IntSize size = drawingArea->size(); + float deviceScaleFactor = m_webPage.deviceScaleFactor(); + size.scale(deviceScaleFactor); + + XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); + ASSERT(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native() == GDK_DISPLAY_XDISPLAY(gdk_display_get_default())); + GdkVisual* visual = gdk_screen_get_rgba_visual(gdk_screen_get_default()); + if (!visual) + visual = gdk_screen_get_system_visual(gdk_screen_get_default()); + m_surface = adoptRef(cairo_xlib_surface_create(display, pixmap, GDK_VISUAL_XVISUAL(visual), size.width(), size.height())); + cairoSurfaceSetDeviceScale(m_surface.get(), deviceScaleFactor, deviceScaleFactor); + m_damage = XDamageCreate(display, pixmap, XDamageReportNonEmpty); + XDamageNotifier::singleton().add(m_damage.get(), [this] { + if (m_webPage.isViewVisible()) + gtk_widget_queue_draw(m_webPage.viewWidget()); + }); + XSync(display, False); +} + +bool AcceleratedBackingStoreX11::paint(cairo_t* cr, const IntRect& clipRect) +{ + if (!m_surface) + return false; + + cairo_save(cr); + AcceleratedBackingStore::paint(cr, clipRect); + + // The surface can be modified by the web process at any time, so we mark it + // as dirty to ensure we always render the updated contents as soon as possible. + cairo_surface_mark_dirty(m_surface.get()); + cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + cairo_set_source_surface(cr, m_surface.get(), 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_fill(cr); + + cairo_restore(cr); + + return true; +} + +} // namespace WebKit + +#endif // USE(REDIRECTED_XCOMPOSITE_WINDOW) diff --git a/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.h b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.h new file mode 100644 index 000000000..d9a7c2f3a --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#include "AcceleratedBackingStore.h" + +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) + +#include <WebCore/RefPtrCairo.h> +#include <WebCore/XUniqueResource.h> + +namespace WebKit { + +class WebPageProxy; + +class AcceleratedBackingStoreX11 final : public AcceleratedBackingStore { + WTF_MAKE_NONCOPYABLE(AcceleratedBackingStoreX11); WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<AcceleratedBackingStoreX11> create(WebPageProxy&); + ~AcceleratedBackingStoreX11(); + +private: + AcceleratedBackingStoreX11(WebPageProxy&); + + void update(const LayerTreeContext&) override; + bool paint(cairo_t*, const WebCore::IntRect&) override; + + RefPtr<cairo_surface_t> m_surface; + WebCore::XUniqueDamage m_damage; +}; + +} // namespace WebKit + +#endif // USE(REDIRECTED_XCOMPOSITE_WINDOW) diff --git a/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.cpp b/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.cpp new file mode 100644 index 000000000..73fb54d37 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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. + */ + +#include "config.h" +#include "DragAndDropHandler.h" + +#if ENABLE(DRAG_SUPPORT) + +#include "WebPageProxy.h" +#include <WebCore/DragData.h> +#include <WebCore/GRefPtrGtk.h> +#include <WebCore/GtkUtilities.h> +#include <WebCore/PasteboardHelper.h> +#include <wtf/RunLoop.h> +#include <wtf/glib/GUniquePtr.h> + +using namespace WebCore; + +namespace WebKit { + +DragAndDropHandler::DragAndDropHandler(WebPageProxy& page) + : m_page(page) +{ +} + +DragAndDropHandler::DroppingContext::DroppingContext(GdkDragContext* gdkContext, const IntPoint& position) + : gdkContext(gdkContext) + , lastMotionPosition(position) + , selectionData(SelectionData::create()) +{ +} + +static inline GdkDragAction dragOperationToGdkDragActions(DragOperation coreAction) +{ + GdkDragAction gdkAction = static_cast<GdkDragAction>(0); + if (coreAction == DragOperationNone) + return gdkAction; + + if (coreAction & DragOperationCopy) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_COPY | gdkAction); + if (coreAction & DragOperationMove) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_MOVE | gdkAction); + if (coreAction & DragOperationLink) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_LINK | gdkAction); + if (coreAction & DragOperationPrivate) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_PRIVATE | gdkAction); + + return gdkAction; +} + +static inline GdkDragAction dragOperationToSingleGdkDragAction(DragOperation coreAction) +{ + if (coreAction == DragOperationEvery || coreAction & DragOperationCopy) + return GDK_ACTION_COPY; + if (coreAction & DragOperationMove) + return GDK_ACTION_MOVE; + if (coreAction & DragOperationLink) + return GDK_ACTION_LINK; + if (coreAction & DragOperationPrivate) + return GDK_ACTION_PRIVATE; + return static_cast<GdkDragAction>(0); +} + +static inline DragOperation gdkDragActionToDragOperation(GdkDragAction gdkAction) +{ + // We have no good way to detect DragOperationEvery other than + // to use it when all applicable flags are on. + if (gdkAction & GDK_ACTION_COPY + && gdkAction & GDK_ACTION_MOVE + && gdkAction & GDK_ACTION_LINK + && gdkAction & GDK_ACTION_PRIVATE) + return DragOperationEvery; + + unsigned action = DragOperationNone; + if (gdkAction & GDK_ACTION_COPY) + action |= DragOperationCopy; + if (gdkAction & GDK_ACTION_MOVE) + action |= DragOperationMove; + if (gdkAction & GDK_ACTION_LINK) + action |= DragOperationLink; + if (gdkAction & GDK_ACTION_PRIVATE) + action |= DragOperationPrivate; + return static_cast<DragOperation>(action); +} + +void DragAndDropHandler::startDrag(Ref<SelectionData>&& selection, DragOperation dragOperation, RefPtr<ShareableBitmap>&& dragImage) +{ +#if GTK_CHECK_VERSION(3, 16, 0) + m_draggingSelectionData = WTFMove(selection); + GRefPtr<GtkTargetList> targetList = PasteboardHelper::singleton().targetListForSelectionData(*m_draggingSelectionData); +#else + RefPtr<SelectionData> selectionData = WTFMove(selection); + GRefPtr<GtkTargetList> targetList = PasteboardHelper::singleton().targetListForSelectionData(*selectionData); +#endif + + GUniquePtr<GdkEvent> currentEvent(gtk_get_current_event()); + GdkDragContext* context = gtk_drag_begin(m_page.viewWidget(), targetList.get(), dragOperationToGdkDragActions(dragOperation), + GDK_BUTTON_PRIMARY, currentEvent.get()); + +#if GTK_CHECK_VERSION(3, 16, 0) + // WebCore::EventHandler does not support more than one DnD operation at the same time for + // a given page, so we should cancel any previous operation whose context we might have + // stored, should we receive a new startDrag event before finishing a previous DnD operation. + if (m_dragContext) + gtk_drag_cancel(m_dragContext.get()); + m_dragContext = context; +#else + // We don't have gtk_drag_cancel() in GTK+ < 3.16, so we use the old code. + // See https://bugs.webkit.org/show_bug.cgi?id=138468 + m_draggingSelectionDataMap.set(context, WTFMove(selectionData)); +#endif + + if (dragImage) { + RefPtr<cairo_surface_t> image(dragImage->createCairoSurface()); + // Use the center of the drag image as hotspot. + cairo_surface_set_device_offset(image.get(), -cairo_image_surface_get_width(image.get()) / 2, -cairo_image_surface_get_height(image.get()) / 2); + gtk_drag_set_icon_surface(context, image.get()); + } else + gtk_drag_set_icon_default(context); +} + +void DragAndDropHandler::fillDragData(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info) +{ +#if GTK_CHECK_VERSION(3, 16, 0) + // This can happen when attempting to call finish drag from webkitWebViewBaseDragDataGet() + // for a obsolete DnD operation that got previously cancelled in startDrag(). + if (m_dragContext.get() != context) + return; + + ASSERT(m_draggingSelectionData); + PasteboardHelper::singleton().fillSelectionData(*m_draggingSelectionData, info, selectionData); +#else + if (auto* selection = m_draggingSelectionDataMap.get(context)) + PasteboardHelper::singleton().fillSelectionData(*selection, info, selectionData); +#endif +} + +void DragAndDropHandler::finishDrag(GdkDragContext* context) +{ +#if GTK_CHECK_VERSION(3, 16, 0) + // This can happen when attempting to call finish drag from webkitWebViewBaseDragEnd() + // for a obsolete DnD operation that got previously cancelled in startDrag(). + if (m_dragContext.get() != context) + return; + + if (!m_draggingSelectionData) + return; + + m_dragContext = nullptr; + m_draggingSelectionData = nullptr; +#else + if (!m_draggingSelectionDataMap.remove(context)) + return; +#endif + + GdkDevice* device = gdk_drag_context_get_device(context); + int x = 0, y = 0; + gdk_device_get_window_at_position(device, &x, &y); + int xRoot = 0, yRoot = 0; + gdk_device_get_position(device, nullptr, &xRoot, &yRoot); + m_page.dragEnded(IntPoint(x, y), IntPoint(xRoot, yRoot), gdkDragActionToDragOperation(gdk_drag_context_get_selected_action(context))); +} + +SelectionData* DragAndDropHandler::dropDataSelection(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, IntPoint& position) +{ + DroppingContext* droppingContext = m_droppingContexts.get(context); + if (!droppingContext) + return nullptr; + + droppingContext->pendingDataRequests--; + PasteboardHelper::singleton().fillSelectionData(selectionData, info, droppingContext->selectionData); + if (droppingContext->pendingDataRequests) + return nullptr; + + // The coordinates passed to drag-data-received signal are sometimes + // inaccurate in WTR, so use the coordinates of the last motion event. + position = droppingContext->lastMotionPosition; + + // If there are no more pending requests, start sending dragging data to WebCore. + return droppingContext->selectionData.ptr(); +} + +void DragAndDropHandler::dragEntered(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, unsigned time) +{ + IntPoint position; + auto* selection = dropDataSelection(context, selectionData, info, position); + if (!selection) + return; + + DragData dragData(selection, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context))); + m_page.resetCurrentDragInformation(); + m_page.dragEntered(dragData); + DragOperation operation = m_page.currentDragOperation(); + gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time); +} + +SelectionData* DragAndDropHandler::dragDataSelection(GdkDragContext* context, const IntPoint& position, unsigned time) +{ + std::unique_ptr<DroppingContext>& droppingContext = m_droppingContexts.add(context, nullptr).iterator->value; + if (!droppingContext) { + GtkWidget* widget = m_page.viewWidget(); + droppingContext = std::make_unique<DroppingContext>(context, position); + Vector<GdkAtom> acceptableTargets(PasteboardHelper::singleton().dropAtomsForContext(widget, droppingContext->gdkContext)); + droppingContext->pendingDataRequests = acceptableTargets.size(); + for (auto& target : acceptableTargets) + gtk_drag_get_data(widget, droppingContext->gdkContext, target, time); + } else + droppingContext->lastMotionPosition = position; + + // Don't send any drag information to WebCore until we've retrieved all the data for this drag operation. + // Otherwise we'd have to block to wait for the drag's data. + if (droppingContext->pendingDataRequests > 0) + return nullptr; + + return droppingContext->selectionData.ptr(); +} + +void DragAndDropHandler::dragMotion(GdkDragContext* context, const IntPoint& position, unsigned time) +{ + auto* selection = dragDataSelection(context, position, time); + if (!selection) + return; + + DragData dragData(selection, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context))); + m_page.dragUpdated(dragData); + DragOperation operation = m_page.currentDragOperation(); + gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time); +} + +void DragAndDropHandler::dragLeave(GdkDragContext* context) +{ + DroppingContext* droppingContext = m_droppingContexts.get(context); + if (!droppingContext) + return; + + // During a drop GTK+ will fire a drag-leave signal right before firing + // the drag-drop signal. We want the actions for drag-leave to happen after + // those for drag-drop, so schedule them to happen asynchronously here. + RunLoop::main().dispatch([this, droppingContext]() { + auto it = m_droppingContexts.find(droppingContext->gdkContext); + if (it == m_droppingContexts.end()) + return; + + // If the view doesn't know about the drag yet (there are still pending data requests), + // don't update it with information about the drag. + if (droppingContext->pendingDataRequests) + return; + + if (!droppingContext->dropHappened) { + // Don't call dragExited if we have just received a drag-drop signal. This + // happens in the case of a successful drop onto the view. + const IntPoint& position = droppingContext->lastMotionPosition; + DragData dragData(droppingContext->selectionData.ptr(), position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), DragOperationNone); + m_page.dragExited(dragData); + m_page.resetCurrentDragInformation(); + } + + m_droppingContexts.remove(it); + }); +} + +bool DragAndDropHandler::drop(GdkDragContext* context, const IntPoint& position, unsigned time) +{ + DroppingContext* droppingContext = m_droppingContexts.get(context); + if (!droppingContext) + return false; + + droppingContext->dropHappened = true; + + uint32_t flags = 0; + if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_COPY) + flags |= WebCore::DragApplicationIsCopyKeyDown; + DragData dragData(droppingContext->selectionData.ptr(), position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)), static_cast<WebCore::DragApplicationFlags>(flags)); + SandboxExtension::Handle handle; + SandboxExtension::HandleArray sandboxExtensionForUpload; + m_page.performDragOperation(dragData, String(), handle, sandboxExtensionForUpload); + gtk_drag_finish(context, TRUE, FALSE, time); + return true; +} + +} // namespace WebKit + +#endif // ENABLE(DRAG_SUPPORT) diff --git a/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.h b/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.h new file mode 100644 index 000000000..1c1ea3a93 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/DragAndDropHandler.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#if ENABLE(DRAG_SUPPORT) + +#include <WebCore/DragActions.h> +#include <WebCore/IntPoint.h> +#include <WebCore/SelectionData.h> +#include <gtk/gtk.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/glib/GRefPtr.h> + +typedef struct _GdkDragContext GdkDragContext; +typedef struct _GtkSelectionData GtkSelectionData; + +namespace WebCore { +class DragData; +class SelectionData; +} + +namespace WebKit { + +class ShareableBitmap; +class WebPageProxy; + +class DragAndDropHandler { + WTF_MAKE_NONCOPYABLE(DragAndDropHandler); +public: + DragAndDropHandler(WebPageProxy&); + + void startDrag(Ref<WebCore::SelectionData>&&, WebCore::DragOperation, RefPtr<ShareableBitmap>&& dragImage); + void fillDragData(GdkDragContext*, GtkSelectionData*, unsigned info); + void finishDrag(GdkDragContext*); + + void dragEntered(GdkDragContext*, GtkSelectionData*, unsigned info, unsigned time); + void dragMotion(GdkDragContext*, const WebCore::IntPoint& position, unsigned time); + void dragLeave(GdkDragContext*); + bool drop(GdkDragContext*, const WebCore::IntPoint& position, unsigned time); + +private: + struct DroppingContext { + DroppingContext(GdkDragContext*, const WebCore::IntPoint& position); + + GdkDragContext* gdkContext { nullptr }; + WebCore::IntPoint lastMotionPosition; + Ref<WebCore::SelectionData> selectionData; + unsigned pendingDataRequests { 0 }; + bool dropHappened { false }; + }; + + WebCore::SelectionData* dropDataSelection(GdkDragContext*, GtkSelectionData*, unsigned info, WebCore::IntPoint& position); + WebCore::SelectionData* dragDataSelection(GdkDragContext*, const WebCore::IntPoint& position, unsigned time); + + WebPageProxy& m_page; + HashMap<GdkDragContext*, std::unique_ptr<DroppingContext>> m_droppingContexts; + +#if GTK_CHECK_VERSION(3, 16, 0) + GRefPtr<GdkDragContext> m_dragContext; + RefPtr<WebCore::SelectionData> m_draggingSelectionData; +#else + // We don't have gtk_drag_cancel() in GTK+ < 3.16, so we use the old code. + // See https://bugs.webkit.org/show_bug.cgi?id=138468 + HashMap<GdkDragContext*, RefPtr<WebCore::SelectionData>> m_draggingSelectionDataMap; +#endif +}; + +} // namespace WebKit + +#endif // ENABLE(DRAG_SUPPORT) diff --git a/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.cpp b/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.cpp deleted file mode 100644 index 1b69302ac..000000000 --- a/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2013, Opera Software ASA. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Opera Software ASA nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 HOLDER 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. - */ - -#include "config.h" -#include "ExperimentalFeatures.h" - -namespace WebKit { - -struct Setting { - ExperimentalFeatures::Feature feature; - const char* featureName; - bool enabled; -}; - -static Setting settings[] = { - { ExperimentalFeatures::CSSGridLayout, "CSS_GRID_LAYOUT", false }, - { ExperimentalFeatures::RegionBasedColumns, "REGION_BASED_COLUMNS", false } -}; - -ExperimentalFeatures::ExperimentalFeatures() -{ - parseEnvironment(); -} - -bool ExperimentalFeatures::isEnabled(Feature feature) -{ - return settings[feature].enabled; -} - -void ExperimentalFeatures::setEnableByName(const String& key, bool enable) -{ - for (unsigned i = 0; i < WTF_ARRAY_LENGTH(settings); i++) { - if (key == settings[i].featureName) { - settings[i].enabled = enable; - break; - } - } -} - -void ExperimentalFeatures::parseEnvironment() -{ - const char* data = getenv("WEBKITGTK_EXPERIMENTAL_FEATURES"); - if (!data) - return; - if (!strcmp(data, "all")) { - for (unsigned i = 0; i < WTF_ARRAY_LENGTH(settings); i++) - settings[i].enabled = true; - } else { - Vector<String> variables; - String(data).split(',', false, variables); - for (unsigned i = 0; i < variables.size(); i++) { - Vector<String> keyAndValue; - variables[i].split('=', false, keyAndValue); - if (keyAndValue.size() != 2) - continue; - setEnableByName(keyAndValue[0], keyAndValue[1][0] - '0'); - } - } -} - -} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.h b/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.h deleted file mode 100644 index 59603cbf8..000000000 --- a/Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2013, Opera Software ASA. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Opera Software ASA nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 HOLDER 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 ExperimentalFeatures_h -#define ExperimentalFeatures_h - -#include <wtf/text/WTFString.h> - -namespace WebKit { - -class ExperimentalFeatures { -public: - enum Feature { - CSSGridLayout, - RegionBasedColumns - }; - - ExperimentalFeatures(); - - bool isEnabled(Feature); - -private: - void setEnableByName(const String& key, bool enable); - void parseEnvironment(); -}; - -} // namespace WebKit - -#endif // ExperimentalFeatures_h diff --git a/Source/WebKit2/UIProcess/gtk/GestureController.cpp b/Source/WebKit2/UIProcess/gtk/GestureController.cpp new file mode 100644 index 000000000..7e1577cba --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/GestureController.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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. + */ + +#include "config.h" +#include "GestureController.h" + +#if HAVE(GTK_GESTURES) + +#include "NativeWebMouseEvent.h" +#include "NativeWebWheelEvent.h" +#include "WebPageProxy.h" +#include <WebCore/FloatPoint.h> +#include <WebCore/Scrollbar.h> +#include <gtk/gtk.h> + +using namespace WebCore; + +namespace WebKit { + +GestureController::GestureController(WebPageProxy& page) + : m_dragGesture(page) + , m_zoomGesture(page) +{ +} + +bool GestureController::handleEvent(const GdkEvent* event) +{ + bool wasProcessingGestures = isProcessingGestures(); + m_dragGesture.handleEvent(event); + m_zoomGesture.handleEvent(event); + return event->type == GDK_TOUCH_END ? wasProcessingGestures : isProcessingGestures(); +} + +bool GestureController::isProcessingGestures() const +{ + return m_dragGesture.isActive() || m_zoomGesture.isActive(); +} + +GestureController::Gesture::Gesture(GtkGesture* gesture, WebPageProxy& page) + : m_gesture(adoptGRef(gesture)) + , m_page(page) +{ + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_gesture.get()), GTK_PHASE_NONE); +} + +bool GestureController::Gesture::isActive() const +{ + return gtk_gesture_is_active(m_gesture.get()); +} + +void GestureController::Gesture::handleEvent(const GdkEvent* event) +{ + gtk_event_controller_handle_event(GTK_EVENT_CONTROLLER(m_gesture.get()), event); +} + +void GestureController::DragGesture::handleDrag(const GdkEvent* event, double x, double y) +{ + ASSERT(m_inDrag); + GUniquePtr<GdkEvent> scrollEvent(gdk_event_new(GDK_SCROLL)); + scrollEvent->scroll.time = event->touch.time; + scrollEvent->scroll.x = m_start.x(); + scrollEvent->scroll.y = m_start.y(); + scrollEvent->scroll.x_root = event->touch.x_root; + scrollEvent->scroll.y_root = event->touch.y_root; + scrollEvent->scroll.direction = GDK_SCROLL_SMOOTH; + scrollEvent->scroll.delta_x = (m_offset.x() - x) / Scrollbar::pixelsPerLineStep(); + scrollEvent->scroll.delta_y = (m_offset.y() - y) / Scrollbar::pixelsPerLineStep(); + scrollEvent->scroll.state = event->touch.state; + m_page.handleWheelEvent(NativeWebWheelEvent(scrollEvent.get())); +} + +void GestureController::DragGesture::handleTap(const GdkEvent* event) +{ + ASSERT(!m_inDrag); + GUniquePtr<GdkEvent> pointerEvent(gdk_event_new(GDK_MOTION_NOTIFY)); + pointerEvent->motion.time = event->touch.time; + pointerEvent->motion.x = event->touch.x; + pointerEvent->motion.y = event->touch.y; + pointerEvent->motion.x_root = event->touch.x_root; + pointerEvent->motion.y_root = event->touch.y_root; + pointerEvent->motion.state = event->touch.state; + m_page.handleMouseEvent(NativeWebMouseEvent(pointerEvent.get(), 0)); + + pointerEvent.reset(gdk_event_new(GDK_BUTTON_PRESS)); + pointerEvent->button.button = 1; + pointerEvent->button.time = event->touch.time; + pointerEvent->button.x = event->touch.x; + pointerEvent->button.y = event->touch.y; + pointerEvent->button.x_root = event->touch.x_root; + pointerEvent->button.y_root = event->touch.y_root; + m_page.handleMouseEvent(NativeWebMouseEvent(pointerEvent.get(), 1)); + + pointerEvent->type = GDK_BUTTON_RELEASE; + m_page.handleMouseEvent(NativeWebMouseEvent(pointerEvent.get(), 0)); +} + +void GestureController::DragGesture::begin(DragGesture* dragGesture, double x, double y, GtkGesture* gesture) +{ + GdkEventSequence* sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + gtk_gesture_set_sequence_state(gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED); + dragGesture->m_inDrag = false; + dragGesture->m_start.set(x, y); + dragGesture->m_offset.set(0, 0); + + GtkWidget* widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); + unsigned delay; + g_object_get(gtk_widget_get_settings(widget), "gtk-long-press-time", &delay, nullptr); + dragGesture->m_longPressTimeout.startOneShot(delay / 1000.0); +} + +void GestureController::DragGesture::update(DragGesture* dragGesture, double x, double y, GtkGesture* gesture) +{ + GdkEventSequence* sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + gtk_gesture_set_sequence_state(gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED); + + GtkWidget* widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); + if (!dragGesture->m_inDrag && gtk_drag_check_threshold(widget, dragGesture->m_start.x(), dragGesture->m_start.y(), dragGesture->m_start.x() + x, dragGesture->m_start.y() + y)) { + dragGesture->m_inDrag = true; + dragGesture->m_longPressTimeout.stop(); + } + + if (dragGesture->m_inDrag) + dragGesture->handleDrag(gtk_gesture_get_last_event(gesture, sequence), x, y); + dragGesture->m_offset.set(x, y); +} + +void GestureController::DragGesture::end(DragGesture* dragGesture, GdkEventSequence* sequence, GtkGesture* gesture) +{ + dragGesture->m_longPressTimeout.stop(); + if (!dragGesture->m_inDrag) { + dragGesture->handleTap(gtk_gesture_get_last_event(gesture, sequence)); + gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + } else if (!gtk_gesture_handles_sequence(gesture, sequence)) + gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); +} + +void GestureController::DragGesture::longPressFired() +{ + m_inDrag = true; +} + +GestureController::DragGesture::DragGesture(WebPageProxy& page) + : Gesture(gtk_gesture_drag_new(page.viewWidget()), page) + , m_longPressTimeout(RunLoop::main(), this, &GestureController::DragGesture::longPressFired) + , m_inDrag(false) +{ + gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_gesture.get()), TRUE); + g_signal_connect_swapped(m_gesture.get(), "drag-begin", G_CALLBACK(begin), this); + g_signal_connect_swapped(m_gesture.get(), "drag-update", G_CALLBACK(update), this); + g_signal_connect_swapped(m_gesture.get(), "end", G_CALLBACK(end), this); +} + +IntPoint GestureController::ZoomGesture::center() const +{ + double x, y; + gtk_gesture_get_bounding_box_center(m_gesture.get(), &x, &y); + return IntPoint(x, y); +} + +void GestureController::ZoomGesture::begin(ZoomGesture* zoomGesture, GdkEventSequence*, GtkGesture* gesture) +{ + gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_CLAIMED); + + zoomGesture->m_initialScale = zoomGesture->m_page.pageScaleFactor(); + zoomGesture->m_page.getCenterForZoomGesture(zoomGesture->center(), zoomGesture->m_initialPoint); +} + +void GestureController::ZoomGesture::handleZoom() +{ + IntPoint scaledOriginOffset = m_viewPoint; + scaledOriginOffset.scale(1 / m_scale, 1 / m_scale); + + IntPoint newOrigin = m_initialPoint; + newOrigin.moveBy(-scaledOriginOffset); + newOrigin.scale(m_scale, m_scale); + + m_page.scalePage(m_scale, newOrigin); +} + +void GestureController::ZoomGesture::scaleChanged(ZoomGesture* zoomGesture, double scale, GtkGesture*) +{ + zoomGesture->m_scale = zoomGesture->m_initialScale * scale; + zoomGesture->m_viewPoint = zoomGesture->center(); + if (zoomGesture->m_idle.isActive()) + return; + + zoomGesture->m_idle.startOneShot(0); +} + +GestureController::ZoomGesture::ZoomGesture(WebPageProxy& page) + : Gesture(gtk_gesture_zoom_new(page.viewWidget()), page) + , m_initialScale(0) + , m_scale(0) + , m_idle(RunLoop::main(), this, &GestureController::ZoomGesture::handleZoom) +{ + g_signal_connect_swapped(m_gesture.get(), "begin", G_CALLBACK(begin), this); + g_signal_connect_swapped(m_gesture.get(), "scale-changed", G_CALLBACK(scaleChanged), this); +} + +} // namespace WebKit + +#endif // HAVE(GTK_GESTURES) diff --git a/Source/WebKit2/UIProcess/gtk/GestureController.h b/Source/WebKit2/UIProcess/gtk/GestureController.h new file mode 100644 index 000000000..86792df42 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/GestureController.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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 GestureController_h +#define GestureController_h + +#if HAVE(GTK_GESTURES) + +#include <WebCore/FloatPoint.h> +#include <wtf/Noncopyable.h> +#include <wtf/RunLoop.h> +#include <wtf/glib/GRefPtr.h> + +typedef union _GdkEvent GdkEvent; +typedef struct _GdkEventSequence GdkEventSequence; +typedef struct _GtkGesture GtkGesture; + +namespace WebKit { +class WebPageProxy; + +class GestureController { + WTF_MAKE_NONCOPYABLE(GestureController); + +public: + GestureController(WebPageProxy&); + + bool isProcessingGestures() const; + bool handleEvent(const GdkEvent*); + +private: + class Gesture { + public: + bool isActive() const; + void handleEvent(const GdkEvent*); + + protected: + Gesture(GtkGesture*, WebPageProxy&); + + GRefPtr<GtkGesture> m_gesture; + WebPageProxy& m_page; + }; + + class DragGesture final : public Gesture { + public: + DragGesture(WebPageProxy&); + + private: + void handleDrag(const GdkEvent*, double x, double y); + void handleTap(const GdkEvent*); + void longPressFired(); + + static void begin(DragGesture*, double x, double y, GtkGesture*); + static void update(DragGesture*, double x, double y, GtkGesture*); + static void end(DragGesture*, GdkEventSequence*, GtkGesture*); + + WebCore::FloatPoint m_start; + WebCore::FloatPoint m_offset; + RunLoop::Timer<DragGesture> m_longPressTimeout; + GRefPtr<GtkGesture> m_longPress; + bool m_inDrag; + }; + + class ZoomGesture final : public Gesture { + public: + ZoomGesture(WebPageProxy&); + + private: + WebCore::IntPoint center() const; + void handleZoom(); + + static void begin(ZoomGesture*, GdkEventSequence*, GtkGesture*); + static void scaleChanged(ZoomGesture*, double scale, GtkGesture*); + + gdouble m_initialScale; + gdouble m_scale; + WebCore::IntPoint m_initialPoint; + WebCore::IntPoint m_viewPoint; + RunLoop::Timer<ZoomGesture> m_idle; + }; + + DragGesture m_dragGesture; + ZoomGesture m_zoomGesture; +}; + +} // namespace WebKit + +#endif // HAVE(GTK_GESTURES) + +#endif // GestureController_h diff --git a/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.cpp b/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.cpp new file mode 100644 index 000000000..e06e629a4 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "HardwareAccelerationManager.h" + +#include "WaylandCompositor.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/PlatformDisplay.h> + +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) +#include <WebCore/PlatformDisplayX11.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +HardwareAccelerationManager& HardwareAccelerationManager::singleton() +{ + static NeverDestroyed<HardwareAccelerationManager> manager; + return manager; +} + +HardwareAccelerationManager::HardwareAccelerationManager() + : m_canUseHardwareAcceleration(true) + , m_forceHardwareAcceleration(false) +{ +#if !ENABLE(OPENGL) + m_canUseHardwareAcceleration = false; + return; +#endif + + const char* disableCompositing = getenv("WEBKIT_DISABLE_COMPOSITING_MODE"); + if (disableCompositing && strcmp(disableCompositing, "0")) { + m_canUseHardwareAcceleration = false; + return; + } + +#if USE(REDIRECTED_XCOMPOSITE_WINDOW) + if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::X11) { + auto& display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()); + std::optional<int> damageBase, errorBase; + if (!display.supportsXComposite() || !display.supportsXDamage(damageBase, errorBase)) { + m_canUseHardwareAcceleration = false; + return; + } + } +#endif + +#if PLATFORM(WAYLAND) && USE(EGL) + if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland) { + if (!WaylandCompositor::singleton().isRunning()) { + m_canUseHardwareAcceleration = false; + return; + } + } +#endif + + const char* forceCompositing = getenv("WEBKIT_FORCE_COMPOSITING_MODE"); + if (forceCompositing && strcmp(forceCompositing, "0")) + m_forceHardwareAcceleration = true; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.h b/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.h new file mode 100644 index 000000000..4fa233b5a --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#include <wtf/NeverDestroyed.h> + +namespace WebKit { + +class HardwareAccelerationManager { + WTF_MAKE_NONCOPYABLE(HardwareAccelerationManager); + friend class NeverDestroyed<HardwareAccelerationManager>; +public: + static HardwareAccelerationManager& singleton(); + + bool canUseHardwareAcceleration() const { return m_canUseHardwareAcceleration; } + bool forceHardwareAcceleration() const { return m_forceHardwareAcceleration; } + +private: + HardwareAccelerationManager(); + + bool m_canUseHardwareAcceleration : 1; + bool m_forceHardwareAcceleration : 1; +}; + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp b/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp new file mode 100644 index 000000000..fc098ac67 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2012, 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "InputMethodFilter.h" + +#include "NativeWebKeyboardEvent.h" +#include "WebPageProxy.h" +#include <WebCore/Color.h> +#include <WebCore/CompositionResults.h> +#include <WebCore/Editor.h> +#include <WebCore/GUniquePtrGtk.h> +#include <WebCore/IntRect.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <wtf/Vector.h> +#include <wtf/glib/GUniquePtr.h> + +using namespace WebCore; + +namespace WebKit { + +void InputMethodFilter::handleCommitCallback(InputMethodFilter* filter, const char* compositionString) +{ + filter->handleCommit(compositionString); +} + +void InputMethodFilter::handlePreeditStartCallback(InputMethodFilter* filter) +{ + filter->handlePreeditStart(); +} + +void InputMethodFilter::handlePreeditChangedCallback(InputMethodFilter* filter) +{ + filter->handlePreeditChanged(); +} + +void InputMethodFilter::handlePreeditEndCallback(InputMethodFilter* filter) +{ + filter->handlePreeditEnd(); +} + +InputMethodFilter::InputMethodFilter() + : m_context(adoptGRef(gtk_im_multicontext_new())) + , m_page(nullptr) + , m_enabled(false) + , m_composingTextCurrently(false) + , m_filteringKeyEvent(false) + , m_preeditChanged(false) + , m_preventNextCommit(false) + , m_justSentFakeKeyUp(false) + , m_cursorOffset(0) + , m_lastFilteredKeyPressCodeWithNoResults(GDK_KEY_VoidSymbol) +#if ENABLE(API_TESTS) + , m_testingMode(false) +#endif +{ + g_signal_connect_swapped(m_context.get(), "commit", G_CALLBACK(handleCommitCallback), this); + g_signal_connect_swapped(m_context.get(), "preedit-start", G_CALLBACK(handlePreeditStartCallback), this); + g_signal_connect_swapped(m_context.get(), "preedit-changed", G_CALLBACK(handlePreeditChangedCallback), this); + g_signal_connect_swapped(m_context.get(), "preedit-end", G_CALLBACK(handlePreeditEndCallback), this); +} + +InputMethodFilter::~InputMethodFilter() +{ + g_signal_handlers_disconnect_matched(m_context.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); +} + +void InputMethodFilter::setEnabled(bool enabled) +{ + ASSERT(m_page); + + // Notify focus out before changing the m_enabled. + if (!enabled) + notifyFocusedOut(); + m_enabled = enabled; + if (enabled) + notifyFocusedIn(); +} + +void InputMethodFilter::setCursorRect(const IntRect& cursorRect) +{ + ASSERT(m_page); + + if (!m_enabled) + return; + + // Don't move the window unless the cursor actually moves more than 10 + // pixels. This prevents us from making the window flash during minor + // cursor adjustments. + static const int windowMovementThreshold = 10 * 10; + if (cursorRect.location().distanceSquaredToPoint(m_lastCareLocation) < windowMovementThreshold) + return; + + m_lastCareLocation = cursorRect.location(); + IntRect translatedRect = cursorRect; + + GtkAllocation allocation; + gtk_widget_get_allocation(m_page->viewWidget(), &allocation); + translatedRect.move(allocation.x, allocation.y); + + GdkRectangle gdkCursorRect = translatedRect; + gtk_im_context_set_cursor_location(m_context.get(), &gdkCursorRect); +} + +void InputMethodFilter::handleKeyboardEvent(GdkEventKey* event, const String& simpleString, EventFakedForComposition faked) +{ +#if ENABLE(API_TESTS) + if (m_testingMode) { + logHandleKeyboardEventForTesting(event, simpleString, faked); + return; + } +#endif + + if (m_filterKeyEventCompletionHandler) { + m_filterKeyEventCompletionHandler(CompositionResults(simpleString), faked); + m_filterKeyEventCompletionHandler = nullptr; + } else + m_page->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event), CompositionResults(simpleString), faked, Vector<String>())); +} + +void InputMethodFilter::handleKeyboardEventWithCompositionResults(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked) +{ +#if ENABLE(API_TESTS) + if (m_testingMode) { + logHandleKeyboardEventWithCompositionResultsForTesting(event, resultsToSend, faked); + return; + } +#endif + + if (m_filterKeyEventCompletionHandler) { + m_filterKeyEventCompletionHandler(CompositionResults(CompositionResults::WillSendCompositionResultsSoon), faked); + m_filterKeyEventCompletionHandler = nullptr; + } else + m_page->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event), CompositionResults(CompositionResults::WillSendCompositionResultsSoon), faked, Vector<String>())); + if (resultsToSend & Composition && !m_confirmedComposition.isNull()) + m_page->confirmComposition(m_confirmedComposition, -1, 0); + + if (resultsToSend & Preedit && !m_preedit.isNull()) { + m_page->setComposition(m_preedit, Vector<CompositionUnderline>{ CompositionUnderline(0, m_preedit.length(), Color(1, 1, 1), false) }, + m_cursorOffset, m_cursorOffset, 0 /* replacement start */, 0 /* replacement end */); + } +} + +void InputMethodFilter::filterKeyEvent(GdkEventKey* event, FilterKeyEventCompletionHandler&& completionHandler) +{ +#if ENABLE(API_TESTS) + ASSERT(m_page || m_testingMode); +#else + ASSERT(m_page); +#endif + m_filterKeyEventCompletionHandler = WTFMove(completionHandler); + if (!m_enabled) { + handleKeyboardEvent(event); + return; + } + + m_preeditChanged = false; + m_filteringKeyEvent = true; + + unsigned lastFilteredKeyPressCodeWithNoResults = m_lastFilteredKeyPressCodeWithNoResults; + m_lastFilteredKeyPressCodeWithNoResults = GDK_KEY_VoidSymbol; + + bool filtered = gtk_im_context_filter_keypress(m_context.get(), event); + m_filteringKeyEvent = false; + + bool justSentFakeKeyUp = m_justSentFakeKeyUp; + m_justSentFakeKeyUp = false; + if (justSentFakeKeyUp && event->type == GDK_KEY_RELEASE) + return; + + // Simple input methods work such that even normal keystrokes fire the + // commit signal. We detect those situations and treat them as normal + // key events, supplying the commit string as the key character. + if (filtered && !m_composingTextCurrently && !m_preeditChanged && m_confirmedComposition.length() == 1) { + handleKeyboardEvent(event, m_confirmedComposition); + m_confirmedComposition = String(); + return; + } + + if (filtered && event->type == GDK_KEY_PRESS) { + if (!m_preeditChanged && m_confirmedComposition.isNull()) { + m_composingTextCurrently = true; + m_lastFilteredKeyPressCodeWithNoResults = event->keyval; + return; + } + + handleKeyboardEventWithCompositionResults(event); + if (!m_confirmedComposition.isEmpty()) { + m_composingTextCurrently = false; + m_confirmedComposition = String(); + } + return; + } + + // If we previously filtered a key press event and it yielded no results. Suppress + // the corresponding key release event to avoid confusing the web content. + if (event->type == GDK_KEY_RELEASE && lastFilteredKeyPressCodeWithNoResults == event->keyval) + return; + + // At this point a keystroke was either: + // 1. Unfiltered + // 2. A filtered keyup event. As the IME code in EditorClient.h doesn't + // ever look at keyup events, we send any composition results before + // the key event. + // Both might have composition results or not. + // + // It's important to send the composition results before the event + // because some IM modules operate that way. For example (taken from + // the Chromium source), the latin-post input method gives this sequence + // when you press 'a' and then backspace: + // 1. keydown 'a' (filtered) + // 2. preedit changed to "a" + // 3. keyup 'a' (unfiltered) + // 4. keydown Backspace (unfiltered) + // 5. commit "a" + // 6. preedit end + if (!m_confirmedComposition.isEmpty()) + confirmComposition(); + if (m_preeditChanged) + updatePreedit(); + handleKeyboardEvent(event); +} + +void InputMethodFilter::confirmComposition() +{ +#if ENABLE(API_TESTS) + if (m_testingMode) { + logConfirmCompositionForTesting(); + m_confirmedComposition = String(); + return; + } +#endif + m_page->confirmComposition(m_confirmedComposition, -1, 0); + m_confirmedComposition = String(); +} + +void InputMethodFilter::updatePreedit() +{ +#if ENABLE(API_TESTS) + if (m_testingMode) { + logSetPreeditForTesting(); + return; + } +#endif + // FIXME: We should parse the PangoAttrList that we get from the IM context here. + m_page->setComposition(m_preedit, Vector<CompositionUnderline>{ CompositionUnderline(0, m_preedit.length(), Color(1, 1, 1), false) }, + m_cursorOffset, m_cursorOffset, 0 /* replacement start */, 0 /* replacement end */); + m_preeditChanged = false; +} + +void InputMethodFilter::notifyFocusedIn() +{ +#if ENABLE(API_TESTS) + ASSERT(m_page || m_testingMode); +#else + ASSERT(m_page); +#endif + if (!m_enabled) + return; + + gtk_im_context_focus_in(m_context.get()); +} + +void InputMethodFilter::notifyFocusedOut() +{ +#if ENABLE(API_TESTS) + ASSERT(m_page || m_testingMode); +#else + ASSERT(m_page); +#endif + if (!m_enabled) + return; + + confirmCurrentComposition(); + cancelContextComposition(); + gtk_im_context_focus_out(m_context.get()); +} + +void InputMethodFilter::notifyMouseButtonPress() +{ +#if ENABLE(API_TESTS) + ASSERT(m_page || m_testingMode); +#else + ASSERT(m_page); +#endif + + // Confirming the composition may trigger a selection change, which + // might trigger further unwanted actions on the context, so we prevent + // that by setting m_composingTextCurrently to false. + confirmCurrentComposition(); + cancelContextComposition(); +} + +void InputMethodFilter::confirmCurrentComposition() +{ + if (!m_composingTextCurrently) + return; + m_page->confirmComposition(String(), -1, 0); + m_composingTextCurrently = false; +} + +void InputMethodFilter::cancelContextComposition() +{ + m_preventNextCommit = !m_preedit.isEmpty(); + + gtk_im_context_reset(m_context.get()); + + m_composingTextCurrently = false; + m_justSentFakeKeyUp = false; + m_preedit = String(); + m_confirmedComposition = String(); +} + +void InputMethodFilter::sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend resultsToSend) +{ + // The Windows composition key event code is 299 or VK_PROCESSKEY. We need to + // emit this code for web compatibility reasons when key events trigger + // composition results. GDK doesn't have an equivalent, so we send VoidSymbol + // here to WebCore. PlatformKeyEvent knows to convert this code into + // VK_PROCESSKEY. + static const int compositionEventKeyCode = GDK_KEY_VoidSymbol; + + GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS)); + event->key.time = GDK_CURRENT_TIME; + event->key.keyval = compositionEventKeyCode; + handleKeyboardEventWithCompositionResults(&event->key, resultsToSend, EventFaked); + + m_confirmedComposition = String(); + if (resultsToSend & Composition) + m_composingTextCurrently = false; + + event->type = GDK_KEY_RELEASE; + handleKeyboardEvent(&event->key, String(), EventFaked); + m_justSentFakeKeyUp = true; +} + +void InputMethodFilter::handleCommit(const char* compositionString) +{ + if (m_preventNextCommit) { + m_preventNextCommit = false; + return; + } + + if (!m_enabled) + return; + + m_confirmedComposition.append(String::fromUTF8(compositionString)); + + // If the commit was triggered outside of a key event, just send + // the IME event now. If we are handling a key event, we'll decide + // later how to handle this. + if (!m_filteringKeyEvent) + sendCompositionAndPreeditWithFakeKeyEvents(Composition); +} + +void InputMethodFilter::handlePreeditStart() +{ + if (m_preventNextCommit || !m_enabled) + return; + m_preeditChanged = true; + m_preedit = emptyString(); +} + +void InputMethodFilter::handlePreeditChanged() +{ + if (!m_enabled) + return; + + GUniqueOutPtr<gchar> newPreedit; + gtk_im_context_get_preedit_string(m_context.get(), &newPreedit.outPtr(), nullptr, &m_cursorOffset); + + if (m_preventNextCommit) { + if (strlen(newPreedit.get()) > 0) + m_preventNextCommit = false; + else + return; + } + + m_preedit = String::fromUTF8(newPreedit.get()); + m_cursorOffset = std::min(std::max(m_cursorOffset, 0), static_cast<int>(m_preedit.length())); + + m_composingTextCurrently = !m_preedit.isEmpty(); + m_preeditChanged = true; + + if (!m_filteringKeyEvent) + sendCompositionAndPreeditWithFakeKeyEvents(Preedit); +} + +void InputMethodFilter::handlePreeditEnd() +{ + if (m_preventNextCommit || !m_enabled) + return; + + m_preedit = String(); + m_cursorOffset = 0; + m_preeditChanged = true; + + if (!m_filteringKeyEvent) + updatePreedit(); +} + +#if ENABLE(API_TESTS) +void InputMethodFilter::logHandleKeyboardEventForTesting(GdkEventKey* event, const String& eventString, EventFakedForComposition faked) +{ + const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press"; + const char* fakedString = faked == EventFaked ? " (faked)" : ""; + if (!eventString.isNull()) + m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x text='%s'%s", eventType, event->keyval, eventString.utf8().data(), fakedString)); + else + m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x%s", eventType, event->keyval, fakedString)); +} + +void InputMethodFilter::logHandleKeyboardEventWithCompositionResultsForTesting(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked) +{ + const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press"; + const char* fakedString = faked == EventFaked ? " (faked)" : ""; + m_events.append(String::format("sendKeyEventWithCompositionResults type=%s keycode=%u%s", eventType, event->keyval, fakedString)); + + if (resultsToSend & Composition && !m_confirmedComposition.isNull()) + logConfirmCompositionForTesting(); + if (resultsToSend & Preedit && !m_preedit.isNull()) + logSetPreeditForTesting(); +} + +void InputMethodFilter::logConfirmCompositionForTesting() +{ + if (m_confirmedComposition.isEmpty()) + m_events.append(String("confirmCurrentcomposition")); + else + m_events.append(String::format("confirmComposition '%s'", m_confirmedComposition.utf8().data())); +} + +void InputMethodFilter::logSetPreeditForTesting() +{ + m_events.append(String::format("setPreedit text='%s' cursorOffset=%i", m_preedit.utf8().data(), m_cursorOffset)); +} +#endif // ENABLE(API_TESTS) + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h b/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h new file mode 100644 index 000000000..f6d66eb5a --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012, 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef InputMethodFilter_h +#define InputMethodFilter_h + +#include <WebCore/IntPoint.h> +#include <wtf/Function.h> +#include <wtf/Noncopyable.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/text/WTFString.h> + +typedef struct _GdkEventKey GdkEventKey; +typedef struct _GtkIMContext GtkIMContext; + +namespace WebCore { +struct CompositionResults; +class IntRect; +} + +namespace WebKit { + +class WebPageProxy; + +class InputMethodFilter { + WTF_MAKE_NONCOPYABLE(InputMethodFilter); +public: + enum EventFakedForComposition { + EventFaked, + EventNotFaked + }; + + InputMethodFilter(); + ~InputMethodFilter(); + + GtkIMContext* context() const { return m_context.get(); } + + void setPage(WebPageProxy* page) { m_page = page; } + + void setEnabled(bool); + void setCursorRect(const WebCore::IntRect&); + + using FilterKeyEventCompletionHandler = Function<void(const WebCore::CompositionResults&, InputMethodFilter::EventFakedForComposition)>; + void filterKeyEvent(GdkEventKey*, FilterKeyEventCompletionHandler&& = nullptr); + void notifyFocusedIn(); + void notifyFocusedOut(); + void notifyMouseButtonPress(); + +#if ENABLE(API_TESTS) + void setTestingMode(bool enable) { m_testingMode = enable; } + const Vector<String>& events() const { return m_events; } +#endif + +private: + enum ResultsToSend { + Preedit = 1 << 1, + Composition = 1 << 2, + PreeditAndComposition = Preedit | Composition + }; + + static void handleCommitCallback(InputMethodFilter*, const char* compositionString); + static void handlePreeditStartCallback(InputMethodFilter*); + static void handlePreeditChangedCallback(InputMethodFilter*); + static void handlePreeditEndCallback(InputMethodFilter*); + + void handleCommit(const char* compositionString); + void handlePreeditChanged(); + void handlePreeditStart(); + void handlePreeditEnd(); + + void handleKeyboardEvent(GdkEventKey*, const String& eventString = String(), EventFakedForComposition = EventNotFaked); + void handleKeyboardEventWithCompositionResults(GdkEventKey*, ResultsToSend = PreeditAndComposition, EventFakedForComposition = EventNotFaked); + + void sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend); + void confirmComposition(); + void updatePreedit(); + void confirmCurrentComposition(); + void cancelContextComposition(); + +#if ENABLE(API_TESTS) + void logHandleKeyboardEventForTesting(GdkEventKey*, const String&, EventFakedForComposition); + void logHandleKeyboardEventWithCompositionResultsForTesting(GdkEventKey*, ResultsToSend, EventFakedForComposition); + void logConfirmCompositionForTesting(); + void logSetPreeditForTesting(); +#endif + + GRefPtr<GtkIMContext> m_context; + WebPageProxy* m_page; + unsigned m_enabled : 1; + unsigned m_composingTextCurrently : 1; + unsigned m_filteringKeyEvent : 1; + unsigned m_preeditChanged : 1; + unsigned m_preventNextCommit : 1; + unsigned m_justSentFakeKeyUp : 1; + int m_cursorOffset; + unsigned m_lastFilteredKeyPressCodeWithNoResults; + WebCore::IntPoint m_lastCareLocation; + String m_confirmedComposition; + String m_preedit; + + FilterKeyEventCompletionHandler m_filterKeyEventCompletionHandler; + +#if ENABLE(API_TESTS) + bool m_testingMode; + Vector<String> m_events; +#endif +}; + +} // namespace WebKit + +#endif // InputMethodFilter_h diff --git a/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp new file mode 100644 index 000000000..f179f16f0 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2010, 2011 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "KeyBindingTranslator.h" + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +namespace WebKit { + +static void backspaceCallback(GtkWidget* widget, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "backspace"); + translator->addPendingEditorCommand("DeleteBackward"); +} + +static void selectAllCallback(GtkWidget* widget, gboolean select, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "select-all"); + translator->addPendingEditorCommand(select ? "SelectAll" : "Unselect"); +} + +static void cutClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "cut-clipboard"); + translator->addPendingEditorCommand("Cut"); +} + +static void copyClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "copy-clipboard"); + translator->addPendingEditorCommand("Copy"); +} + +static void pasteClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "paste-clipboard"); + translator->addPendingEditorCommand("Paste"); +} + +static void toggleOverwriteCallback(GtkWidget* widget, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "toggle-overwrite"); + translator->addPendingEditorCommand("OverWrite"); +} + +// GTK+ will still send these signals to the web view. So we can safely stop signal +// emission without breaking accessibility. +static void popupMenuCallback(GtkWidget* widget, KeyBindingTranslator*) +{ + g_signal_stop_emission_by_name(widget, "popup-menu"); +} + +static void showHelpCallback(GtkWidget* widget, KeyBindingTranslator*) +{ + g_signal_stop_emission_by_name(widget, "show-help"); +} + +static const char* const gtkDeleteCommands[][2] = { + { "DeleteBackward", "DeleteForward" }, // Characters + { "DeleteWordBackward", "DeleteWordForward" }, // Word ends + { "DeleteWordBackward", "DeleteWordForward" }, // Words + { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Lines + { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Line ends + { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraph ends + { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraphs + { 0, 0 } // Whitespace (M-\ in Emacs) +}; + +static void deleteFromCursorCallback(GtkWidget* widget, GtkDeleteType deleteType, gint count, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "delete-from-cursor"); + int direction = count > 0 ? 1 : 0; + + // Ensuring that deleteType <= G_N_ELEMENTS here results in a compiler warning + // that the condition is always true. + + if (deleteType == GTK_DELETE_WORDS) { + if (!direction) { + translator->addPendingEditorCommand("MoveWordForward"); + translator->addPendingEditorCommand("MoveWordBackward"); + } else { + translator->addPendingEditorCommand("MoveWordBackward"); + translator->addPendingEditorCommand("MoveWordForward"); + } + } else if (deleteType == GTK_DELETE_DISPLAY_LINES) { + if (!direction) + translator->addPendingEditorCommand("MoveToBeginningOfLine"); + else + translator->addPendingEditorCommand("MoveToEndOfLine"); + } else if (deleteType == GTK_DELETE_PARAGRAPHS) { + if (!direction) + translator->addPendingEditorCommand("MoveToBeginningOfParagraph"); + else + translator->addPendingEditorCommand("MoveToEndOfParagraph"); + } + + const char* rawCommand = gtkDeleteCommands[deleteType][direction]; + if (!rawCommand) + return; + + for (int i = 0; i < abs(count); i++) + translator->addPendingEditorCommand(rawCommand); +} + +static const char* const gtkMoveCommands[][4] = { + { "MoveBackward", "MoveForward", + "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Forward/backward grapheme + { "MoveLeft", "MoveRight", + "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Left/right grapheme + { "MoveWordBackward", "MoveWordForward", + "MoveWordBackwardAndModifySelection", "MoveWordForwardAndModifySelection" }, // Forward/backward word + { "MoveUp", "MoveDown", + "MoveUpAndModifySelection", "MoveDownAndModifySelection" }, // Up/down line + { "MoveToBeginningOfLine", "MoveToEndOfLine", + "MoveToBeginningOfLineAndModifySelection", "MoveToEndOfLineAndModifySelection" }, // Up/down line ends + { 0, 0, + "MoveParagraphBackwardAndModifySelection", "MoveParagraphForwardAndModifySelection" }, // Up/down paragraphs + { "MoveToBeginningOfParagraph", "MoveToEndOfParagraph", + "MoveToBeginningOfParagraphAndModifySelection", "MoveToEndOfParagraphAndModifySelection" }, // Up/down paragraph ends. + { "MovePageUp", "MovePageDown", + "MovePageUpAndModifySelection", "MovePageDownAndModifySelection" }, // Up/down page + { "MoveToBeginningOfDocument", "MoveToEndOfDocument", + "MoveToBeginningOfDocumentAndModifySelection", "MoveToEndOfDocumentAndModifySelection" }, // Begin/end of buffer + { 0, 0, + 0, 0 } // Horizontal page movement +}; + +static void moveCursorCallback(GtkWidget* widget, GtkMovementStep step, gint count, gboolean extendSelection, KeyBindingTranslator* translator) +{ + g_signal_stop_emission_by_name(widget, "move-cursor"); + int direction = count > 0 ? 1 : 0; + if (extendSelection) + direction += 2; + + if (static_cast<unsigned>(step) >= G_N_ELEMENTS(gtkMoveCommands)) + return; + + const char* rawCommand = gtkMoveCommands[step][direction]; + if (!rawCommand) + return; + + for (int i = 0; i < abs(count); i++) + translator->addPendingEditorCommand(rawCommand); +} + +KeyBindingTranslator::KeyBindingTranslator() + : m_nativeWidget(gtk_text_view_new()) +{ + g_signal_connect(m_nativeWidget.get(), "backspace", G_CALLBACK(backspaceCallback), this); + g_signal_connect(m_nativeWidget.get(), "cut-clipboard", G_CALLBACK(cutClipboardCallback), this); + g_signal_connect(m_nativeWidget.get(), "copy-clipboard", G_CALLBACK(copyClipboardCallback), this); + g_signal_connect(m_nativeWidget.get(), "paste-clipboard", G_CALLBACK(pasteClipboardCallback), this); + g_signal_connect(m_nativeWidget.get(), "select-all", G_CALLBACK(selectAllCallback), this); + g_signal_connect(m_nativeWidget.get(), "move-cursor", G_CALLBACK(moveCursorCallback), this); + g_signal_connect(m_nativeWidget.get(), "delete-from-cursor", G_CALLBACK(deleteFromCursorCallback), this); + g_signal_connect(m_nativeWidget.get(), "toggle-overwrite", G_CALLBACK(toggleOverwriteCallback), this); + g_signal_connect(m_nativeWidget.get(), "popup-menu", G_CALLBACK(popupMenuCallback), this); + g_signal_connect(m_nativeWidget.get(), "show-help", G_CALLBACK(showHelpCallback), this); +} + +struct KeyCombinationEntry { + unsigned gdkKeyCode; + unsigned state; + const char* name; +}; + +static const KeyCombinationEntry customKeyBindings[] = { + { GDK_KEY_b, GDK_CONTROL_MASK, "ToggleBold" }, + { GDK_KEY_i, GDK_CONTROL_MASK, "ToggleItalic" }, + { GDK_KEY_Escape, 0, "Cancel" }, + { GDK_KEY_greater, GDK_CONTROL_MASK, "Cancel" }, + { GDK_KEY_Tab, 0, "InsertTab" }, + { GDK_KEY_Tab, GDK_SHIFT_MASK, "InsertBacktab" }, +}; + +Vector<String> KeyBindingTranslator::commandsForKeyEvent(GdkEventKey* event) +{ + ASSERT(m_pendingEditorCommands.isEmpty()); + + gtk_bindings_activate_event(G_OBJECT(m_nativeWidget.get()), event); + if (!m_pendingEditorCommands.isEmpty()) + return WTFMove(m_pendingEditorCommands); + + // Special-case enter keys for we want them to work regardless of modifier. + if ((event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter || event->keyval == GDK_KEY_ISO_Enter)) + return { "InsertNewLine" }; + + // For keypress events, we want charCode(), but keyCode() does that. + unsigned mapKey = event->state << 16 | event->keyval; + if (!mapKey) + return { }; + + for (unsigned i = 0; i < G_N_ELEMENTS(customKeyBindings); ++i) { + if (mapKey == (customKeyBindings[i].state << 16 | customKeyBindings[i].gdkKeyCode)) + return { customKeyBindings[i].name }; + } + + return { }; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.h b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.h new file mode 100644 index 000000000..b2ded5de6 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010, 2011 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef KeyBindingTranslator_h +#define KeyBindingTranslator_h + +#include <WebCore/GRefPtrGtk.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +typedef struct _GdkEventKey GdkEventKey; + +namespace WebKit { + +class KeyBindingTranslator { +public: + KeyBindingTranslator(); + + Vector<String> commandsForKeyEvent(GdkEventKey*); + void addPendingEditorCommand(const char* command) { m_pendingEditorCommands.append(command); } + +private: + GRefPtr<GtkWidget> m_nativeWidget; + Vector<String> m_pendingEditorCommands; +}; + +} // namespace WebKit + +#endif + + diff --git a/Source/WebKit2/UIProcess/gtk/TextCheckerGtk.cpp b/Source/WebKit2/UIProcess/gtk/TextCheckerGtk.cpp index 6bc419079..2513cbd69 100644 --- a/Source/WebKit2/UIProcess/gtk/TextCheckerGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/TextCheckerGtk.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2010 Apple Inc. All rights reserved. * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. + * Copyright (C) 2011-2013 Samsung Electronics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,119 +29,290 @@ #include "TextChecker.h" #include "TextCheckerState.h" -#include "WebTextChecker.h" +#include "WebProcessPool.h" #include <WebCore/NotImplemented.h> +#include <WebCore/TextCheckerEnchant.h> +#include <unicode/ubrk.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/TextBreakIterator.h> using namespace WebCore; - + namespace WebKit { -static TextCheckerState textCheckerState; +#if ENABLE(SPELLCHECK) +static WebCore::TextCheckerEnchant& enchantTextChecker() +{ + static NeverDestroyed<WebCore::TextCheckerEnchant> checker; + return checker; +} +#endif -const TextCheckerState& TextChecker::state() +TextCheckerState& checkerState() { - static bool didInitializeState = false; - if (didInitializeState) - return textCheckerState; + static TextCheckerState textCheckerState; + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + textCheckerState.isContinuousSpellCheckingEnabled = false; + textCheckerState.isGrammarCheckingEnabled = false; + }); + + return textCheckerState; +} - WebTextCheckerClient& client = WebTextChecker::shared()->client(); - textCheckerState.isContinuousSpellCheckingEnabled = client.continuousSpellCheckingEnabled(); - textCheckerState.isGrammarCheckingEnabled = client.grammarCheckingEnabled(); +const TextCheckerState& TextChecker::state() +{ + return checkerState(); +} + +static bool testingModeEnabled = false; + +void TextChecker::setTestingMode(bool enabled) +{ + testingModeEnabled = enabled; +} - didInitializeState = true; +bool TextChecker::isTestingMode() +{ + return testingModeEnabled; +} - return textCheckerState; +#if ENABLE(SPELLCHECK) +static void updateStateForAllProcessPools() +{ + for (const auto& processPool : WebProcessPool::allProcessPools()) + processPool->textCheckerStateChanged(); } - +#endif + bool TextChecker::isContinuousSpellCheckingAllowed() { - return WebTextChecker::shared()->client().continuousSpellCheckingAllowed(); +#if ENABLE(SPELLCHECK) + return true; +#else + return false; +#endif } void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled) { - if (state().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled) +#if ENABLE(SPELLCHECK) + if (checkerState().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled) return; - textCheckerState.isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled; - WebTextChecker::shared()->client().setContinuousSpellCheckingEnabled(isContinuousSpellCheckingEnabled); + checkerState().isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled; + updateStateForAllProcessPools(); +#else + UNUSED_PARAM(isContinuousSpellCheckingEnabled); +#endif } void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled) { - if (state().isGrammarCheckingEnabled == isGrammarCheckingEnabled) +#if ENABLE(SPELLCHECK) + if (checkerState().isGrammarCheckingEnabled == isGrammarCheckingEnabled) return; - textCheckerState.isGrammarCheckingEnabled = isGrammarCheckingEnabled; - WebTextChecker::shared()->client().setGrammarCheckingEnabled(isGrammarCheckingEnabled); + checkerState().isGrammarCheckingEnabled = isGrammarCheckingEnabled; + updateStateForAllProcessPools(); +#else + UNUSED_PARAM(isGrammarCheckingEnabled); +#endif } void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled) { - textCheckerState.isContinuousSpellCheckingEnabled = enabled; +#if ENABLE(SPELLCHECK) + checkerState().isContinuousSpellCheckingEnabled = enabled; +#else + UNUSED_PARAM(enabled); +#endif } void TextChecker::grammarCheckingEnabledStateChanged(bool enabled) { - textCheckerState.isGrammarCheckingEnabled = enabled; +#if ENABLE(SPELLCHECK) + checkerState().isGrammarCheckingEnabled = enabled; +#else + UNUSED_PARAM(enabled); +#endif } -int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy* page) +int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy*) { - return WebTextChecker::shared()->client().uniqueSpellDocumentTag(page); + return 0; } -void TextChecker::closeSpellDocumentWithTag(int64_t tag) +void TextChecker::closeSpellDocumentWithTag(int64_t /* tag */) { - WebTextChecker::shared()->client().closeSpellDocumentWithTag(tag); } -void TextChecker::checkSpellingOfString(int64_t spellDocumentTag, const UChar* text, uint32_t length, int32_t& misspellingLocation, int32_t& misspellingLength) +void TextChecker::checkSpellingOfString(int64_t /* spellDocumentTag */, StringView text, int32_t& misspellingLocation, int32_t& misspellingLength) { - WebTextChecker::shared()->client().checkSpellingOfString(spellDocumentTag, String(text, length), misspellingLocation, misspellingLength); +#if ENABLE(SPELLCHECK) + misspellingLocation = -1; + misspellingLength = 0; + enchantTextChecker().checkSpellingOfString(text.toStringWithoutCopying(), misspellingLocation, misspellingLength); +#else + UNUSED_PARAM(text); + UNUSED_PARAM(misspellingLocation); + UNUSED_PARAM(misspellingLength); +#endif } -void TextChecker::checkGrammarOfString(int64_t spellDocumentTag, const UChar* text, uint32_t length, Vector<WebCore::GrammarDetail>& grammarDetails, int32_t& badGrammarLocation, int32_t& badGrammarLength) +void TextChecker::checkGrammarOfString(int64_t /* spellDocumentTag */, StringView /* text */, Vector<WebCore::GrammarDetail>& /* grammarDetails */, int32_t& /* badGrammarLocation */, int32_t& /* badGrammarLength */) { - WebTextChecker::shared()->client().checkGrammarOfString(spellDocumentTag, String(text, length), grammarDetails, badGrammarLocation, badGrammarLength); } bool TextChecker::spellingUIIsShowing() { - return WebTextChecker::shared()->client().spellingUIIsShowing(); + return false; } void TextChecker::toggleSpellingUIIsShowing() { - WebTextChecker::shared()->client().toggleSpellingUIIsShowing(); } -void TextChecker::updateSpellingUIWithMisspelledWord(int64_t spellDocumentTag, const String& misspelledWord) +void TextChecker::updateSpellingUIWithMisspelledWord(int64_t /* spellDocumentTag */, const String& /* misspelledWord */) +{ +} + +void TextChecker::updateSpellingUIWithGrammarString(int64_t /* spellDocumentTag */, const String& /* badGrammarPhrase */, const GrammarDetail& /* grammarDetail */) { - WebTextChecker::shared()->client().updateSpellingUIWithMisspelledWord(spellDocumentTag, misspelledWord); } -void TextChecker::updateSpellingUIWithGrammarString(int64_t spellDocumentTag, const String& badGrammarPhrase, const GrammarDetail& grammarDetail) +void TextChecker::getGuessesForWord(int64_t /* spellDocumentTag */, const String& word, const String& /* context */, int32_t /* insertionPoint */, Vector<String>& guesses, bool) { - WebTextChecker::shared()->client().updateSpellingUIWithGrammarString(spellDocumentTag, badGrammarPhrase, grammarDetail); +#if ENABLE(SPELLCHECK) + guesses = enchantTextChecker().getGuessesForWord(word); +#else + UNUSED_PARAM(word); + UNUSED_PARAM(guesses); +#endif } -void TextChecker::getGuessesForWord(int64_t spellDocumentTag, const String& word, const String& context, Vector<String>& guesses) +void TextChecker::learnWord(int64_t /* spellDocumentTag */, const String& word) { - WebTextChecker::shared()->client().guessesForWord(spellDocumentTag, word, guesses); +#if ENABLE(SPELLCHECK) + enchantTextChecker().learnWord(word); +#else + UNUSED_PARAM(word); +#endif } -void TextChecker::learnWord(int64_t spellDocumentTag, const String& word) +void TextChecker::ignoreWord(int64_t /* spellDocumentTag */, const String& word) { - WebTextChecker::shared()->client().learnWord(spellDocumentTag, word); +#if ENABLE(SPELLCHECK) + enchantTextChecker().ignoreWord(word); +#else + UNUSED_PARAM(word); +#endif +} + +void TextChecker::requestCheckingOfString(PassRefPtr<TextCheckerCompletion> completion, int32_t insertionPoint) +{ +#if ENABLE(SPELLCHECK) + if (!completion) + return; + + TextCheckingRequestData request = completion->textCheckingRequestData(); + ASSERT(request.sequence() != unrequestedTextCheckingSequence); + ASSERT(request.mask() != TextCheckingTypeNone); + + completion->didFinishCheckingText(checkTextOfParagraph(completion->spellDocumentTag(), request.text(), insertionPoint, request.mask(), false)); +#else + UNUSED_PARAM(completion); +#endif +} + +#if USE(UNIFIED_TEXT_CHECKING) && ENABLE(SPELLCHECK) +static unsigned nextWordOffset(StringView text, unsigned currentOffset) +{ + // FIXME: avoid creating textIterator object here, it could be passed as a parameter. + // ubrk_isBoundary() leaves the iterator pointing to the first boundary position at + // or after "offset" (ubrk_isBoundary side effect). + // For many word separators, the method doesn't properly determine the boundaries + // without resetting the iterator. + UBreakIterator* textIterator = wordBreakIterator(text); + if (!textIterator) + return currentOffset; + + unsigned wordOffset = currentOffset; + while (wordOffset < text.length() && ubrk_isBoundary(textIterator, wordOffset)) + ++wordOffset; + + // Do not treat the word's boundary as a separator. + if (!currentOffset && wordOffset == 1) + return currentOffset; + + // Omit multiple separators. + if ((wordOffset - currentOffset) > 1) + --wordOffset; + + return wordOffset; +} +#endif + +#if USE(UNIFIED_TEXT_CHECKING) +Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, StringView text, int32_t insertionPoint, uint64_t checkingTypes, bool) +{ + UNUSED_PARAM(insertionPoint); +#if ENABLE(SPELLCHECK) + if (!(checkingTypes & TextCheckingTypeSpelling)) + return Vector<TextCheckingResult>(); + + UBreakIterator* textIterator = wordBreakIterator(text); + if (!textIterator) + return Vector<TextCheckingResult>(); + + // Omit the word separators at the beginning/end of the text to don't unnecessarily + // involve the client to check spelling for them. + unsigned offset = nextWordOffset(text, 0); + unsigned lengthStrip = text.length(); + while (lengthStrip > 0 && ubrk_isBoundary(textIterator, lengthStrip - 1)) + --lengthStrip; + + Vector<TextCheckingResult> paragraphCheckingResult; + while (offset < lengthStrip) { + int32_t misspellingLocation = -1; + int32_t misspellingLength = 0; + checkSpellingOfString(spellDocumentTag, text.substring(offset, lengthStrip - offset), misspellingLocation, misspellingLength); + if (!misspellingLength) + break; + + TextCheckingResult misspellingResult; + misspellingResult.type = TextCheckingTypeSpelling; + misspellingResult.location = offset + misspellingLocation; + misspellingResult.length = misspellingLength; + paragraphCheckingResult.append(misspellingResult); + offset += misspellingLocation + misspellingLength; + // Generally, we end up checking at the word separator, move to the adjacent word. + offset = nextWordOffset(text.substring(0, lengthStrip), offset); + } + return paragraphCheckingResult; +#else + UNUSED_PARAM(spellDocumentTag); + UNUSED_PARAM(text); + UNUSED_PARAM(checkingTypes); + return Vector<TextCheckingResult>(); +#endif // ENABLE(SPELLCHECK) } +#endif // USE(UNIFIED_TEXT_CHECKING) -void TextChecker::ignoreWord(int64_t spellDocumentTag, const String& word) +void TextChecker::setSpellCheckingLanguages(const Vector<String>& languages) { - WebTextChecker::shared()->client().ignoreWord(spellDocumentTag, word); +#if ENABLE(SPELLCHECK) + enchantTextChecker().updateSpellCheckingLanguages(languages); +#else + UNUSED_PARAM(languages); +#endif } -void TextChecker::requestCheckingOfString(PassRefPtr<TextCheckerCompletion>) +Vector<String> TextChecker::loadedSpellCheckingLanguages() { - notImplemented(); +#if ENABLE(SPELLCHECK) + return enchantTextChecker().loadedSpellCheckingLanguages(); +#else + return Vector<String>(); +#endif } } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp new file mode 100644 index 000000000..99e1588b4 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "WaylandCompositor.h" + +#if PLATFORM(WAYLAND) && USE(EGL) + +#include "WebKit2WaylandServerProtocol.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <WebCore/GLContext.h> +#include <WebCore/PlatformDisplayWayland.h> +#include <WebCore/Region.h> +#include <WebCore/UUID.h> +#include <wayland-server-protocol.h> + +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#else +#include <WebCore/OpenGLShims.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +#if !defined(PFNEGLBINDWAYLANDDISPLAYWL) +typedef EGLBoolean (*PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*); +#endif + +#if !defined(PFNEGLUNBINDWAYLANDDISPLAYWL) +typedef EGLBoolean (*PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*); +#endif + +#if !defined(PFNEGLQUERYWAYLANDBUFFERWL) +typedef EGLBoolean (*PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay, struct wl_resource*, EGLint attribute, EGLint* value); +#endif + +#if !defined(PFNEGLCREATEIMAGEKHRPROC) +typedef EGLImageKHR (*PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay, EGLContext, EGLenum target, EGLClientBuffer, const EGLint* attribList); +#endif + +#if !defined(PFNEGLDESTROYIMAGEKHRPROC) +typedef EGLBoolean (*PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay, EGLImageKHR); +#endif + +#if !defined(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) +typedef void (*PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES); +#endif + +static PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplay; +static PFNEGLUNBINDWAYLANDDISPLAYWL eglUnbindWaylandDisplay; +static PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBuffer; +static PFNEGLCREATEIMAGEKHRPROC eglCreateImage; +static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImage; +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glImageTargetTexture2D; + +WaylandCompositor& WaylandCompositor::singleton() +{ + static NeverDestroyed<WaylandCompositor> waylandCompositor; + return waylandCompositor; +} + +WaylandCompositor::Buffer* WaylandCompositor::Buffer::getOrCreate(struct wl_resource* resource) +{ + if (struct wl_listener* listener = wl_resource_get_destroy_listener(resource, destroyListenerCallback)) { + WaylandCompositor::Buffer* buffer; + return wl_container_of(listener, buffer, m_destroyListener); + } + + return new WaylandCompositor::Buffer(resource); +} + +WaylandCompositor::Buffer::Buffer(struct wl_resource* resource) + : m_resource(resource) + , m_weakPtrFactory(this) +{ + wl_list_init(&m_destroyListener.link); + m_destroyListener.notify = destroyListenerCallback; + wl_resource_add_destroy_listener(m_resource, &m_destroyListener); +} + +WaylandCompositor::Buffer::~Buffer() +{ + wl_list_remove(&m_destroyListener.link); +} + +void WaylandCompositor::Buffer::destroyListenerCallback(struct wl_listener* listener, void*) +{ + WaylandCompositor::Buffer* buffer; + buffer = wl_container_of(listener, buffer, m_destroyListener); + delete buffer; +} + +void WaylandCompositor::Buffer::use() +{ + m_busyCount++; +} + +void WaylandCompositor::Buffer::unuse() +{ + m_busyCount--; + if (!m_busyCount) + wl_resource_queue_event(m_resource, WL_BUFFER_RELEASE); +} + +EGLImageKHR WaylandCompositor::Buffer::createImage() const +{ + return static_cast<EGLImageKHR*>(eglCreateImage(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, m_resource, nullptr)); +} + +IntSize WaylandCompositor::Buffer::size() const +{ + EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay(); + int width, height; + eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_WIDTH, &width); + eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_HEIGHT, &height); + + return { width, height }; +} + +WaylandCompositor::Surface::Surface() + : m_image(EGL_NO_IMAGE_KHR) +{ + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_2D, m_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +WaylandCompositor::Surface::~Surface() +{ + // Destroy pending frame callbacks. + auto list = WTFMove(m_frameCallbackList); + for (auto* resource : list) + wl_resource_destroy(resource); + + if (m_buffer) + m_buffer->unuse(); + + if (m_image != EGL_NO_IMAGE_KHR) + eglDestroyImage(PlatformDisplay::sharedDisplay().eglDisplay(), m_image); + + glDeleteTextures(1, &m_texture); +} + +void WaylandCompositor::Surface::makePendingBufferCurrent() +{ + if (m_pendingBuffer == m_buffer) + return; + + if (m_buffer) + m_buffer->unuse(); + + if (m_pendingBuffer) + m_pendingBuffer->use(); + + m_buffer = m_pendingBuffer; +} + +void WaylandCompositor::Surface::attachBuffer(struct wl_resource* buffer) +{ + if (m_pendingBuffer) + m_pendingBuffer = nullptr; + + if (buffer) { + auto* compositorBuffer = WaylandCompositor::Buffer::getOrCreate(buffer); + m_pendingBuffer = compositorBuffer->createWeakPtr(); + } +} + +void WaylandCompositor::Surface::requestFrame(struct wl_resource* resource) +{ + wl_resource_set_implementation(resource, nullptr, this, [](struct wl_resource* resource) { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (size_t item = surface->m_frameCallbackList.find(resource) != notFound) + surface->m_frameCallbackList.remove(item); + }); + m_frameCallbackList.append(resource); +} + +bool WaylandCompositor::Surface::prepareTextureForPainting(unsigned& texture, IntSize& textureSize) +{ + if (m_image == EGL_NO_IMAGE_KHR) + return false; + + glBindTexture(GL_TEXTURE_2D, m_texture); + glImageTargetTexture2D(GL_TEXTURE_2D, m_image); + + texture = m_texture; + textureSize = m_imageSize; + return true; +} + +void WaylandCompositor::Surface::commit() +{ + EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay(); + if (m_image != EGL_NO_IMAGE_KHR) + eglDestroyImage(eglDisplay, m_image); + m_image = m_pendingBuffer->createImage(); + if (m_image == EGL_NO_IMAGE_KHR) + return; + + m_imageSize = m_pendingBuffer->size(); + + makePendingBufferCurrent(); + if (m_webPage) + m_webPage->setViewNeedsDisplay(IntRect(IntPoint::zero(), m_webPage->viewSize())); + + // From a Wayland point-of-view frame callbacks should be fired where the + // compositor knows it has *used* the committed contents, so firing them here + // can be surprising but we don't need them as a throttling mechanism because + // rendering synchronization is handled elsewhere by WebKit. + auto list = WTFMove(m_frameCallbackList); + for (auto* resource : list) { + wl_callback_send_done(resource, 0); + wl_resource_destroy(resource); + } +} + +static const struct wl_surface_interface surfaceInterface = { + // destroyCallback + [](struct wl_client*, struct wl_resource* resource) + { + wl_resource_destroy(resource); + }, + // attachCallback + [](struct wl_client* client, struct wl_resource* resource, struct wl_resource* buffer, int32_t sx, int32_t sy) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + + EGLint format; + if (!eglQueryWaylandBuffer(PlatformDisplay::sharedDisplay().eglDisplay(), buffer, EGL_TEXTURE_FORMAT, &format) + || (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA)) + return; + + surface->attachBuffer(buffer); + }, + // damageCallback + [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, + // frameCallback + [](struct wl_client* client, struct wl_resource* resource, uint32_t id) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + + if (struct wl_resource* callbackResource = wl_resource_create(client, &wl_callback_interface, 1, id)) + surface->requestFrame(callbackResource); + else + wl_client_post_no_memory(client); + }, + // setOpaqueRegionCallback + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + // setInputRegionCallback + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + // commitCallback + [](struct wl_client* client, struct wl_resource* resource) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + surface->commit(); + }, + // setBufferTransformCallback + [](struct wl_client*, struct wl_resource*, int32_t) { }, + // setBufferScaleCallback + [](struct wl_client*, struct wl_resource*, int32_t) { }, +#if WAYLAND_VERSION_MAJOR > 1 || (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR >= 10) + // damageBufferCallback + [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, +#endif +}; + +static const struct wl_compositor_interface compositorInterface = { + // createSurfaceCallback + [](struct wl_client* client, struct wl_resource* resource, uint32_t id) + { + if (struct wl_resource* surfaceResource = wl_resource_create(client, &wl_surface_interface, 1, id)) { + wl_resource_set_implementation(surfaceResource, &surfaceInterface, new WaylandCompositor::Surface(), + [](struct wl_resource* resource) { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + delete surface; + }); + } else + wl_client_post_no_memory(client); + }, + // createRegionCallback + [](struct wl_client*, struct wl_resource*, uint32_t) { } +}; + +static const struct wl_webkitgtk_interface webkitgtkInterface = { + // bindSurfaceToPageCallback + [](struct wl_client*, struct wl_resource* resource, struct wl_resource* surfaceResource, uint32_t pageID) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(surfaceResource)); + if (!surface) + return; + + auto* compositor = static_cast<WaylandCompositor*>(wl_resource_get_user_data(resource)); + compositor->bindSurfaceToWebPage(surface, pageID); + } +}; + +bool WaylandCompositor::initializeEGL() +{ + if (PlatformDisplay::sharedDisplay().eglCheckVersion(1, 5)) { + eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImage")); + eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImage")); + } else { + const char* extensions = eglQueryString(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_EXTENSIONS); + if (GLContext::isExtensionSupported(extensions, "EGL_KHR_image_base")) { + eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + } + } + if (!eglCreateImage || !eglDestroyImage) { + WTFLogAlways("WaylandCompositor requires eglCreateImage and eglDestroyImage."); + return false; + } + + glImageTargetTexture2D = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + if (!glImageTargetTexture2D) { + WTFLogAlways("WaylandCompositor requires glEGLImageTargetTexture2D."); + return false; + } + + eglQueryWaylandBuffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL")); + if (!eglQueryWaylandBuffer) { + WTFLogAlways("WaylandCompositor requires eglQueryWaylandBuffer."); + return false; + } + + eglBindWaylandDisplay = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL")); + eglUnbindWaylandDisplay = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL")); + if (!eglBindWaylandDisplay || !eglUnbindWaylandDisplay) { + WTFLogAlways("WaylandCompositor requires eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL."); + return false; + } + + m_eglContext = GLContext::createOffscreenContext(); + if (!m_eglContext) + return false; + + if (!m_eglContext->makeContextCurrent()) + return false; + + return true; +} + +typedef struct { + GSource source; + gpointer fdTag; + struct wl_display* display; +} WaylandLoopSource; + +static const unsigned waylandLoopSourceCondition = G_IO_IN | G_IO_HUP | G_IO_ERR; + +static GSourceFuncs waylandLoopSourceFunctions = { + // prepare + [](GSource *source, int *timeout) -> gboolean + { + *timeout = -1; + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source); + wl_display_flush_clients(wlLoopSource->display); + return FALSE; + }, + nullptr, // check + // dispatch + [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean + { + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source); + unsigned events = g_source_query_unix_fd(source, wlLoopSource->fdTag) & waylandLoopSourceCondition; + if (events & G_IO_HUP || events & G_IO_ERR) { + WTFLogAlways("Wayland Display Event Source: lost connection to nested Wayland compositor"); + return G_SOURCE_REMOVE; + } + + if (events & G_IO_IN) + wl_event_loop_dispatch(wl_display_get_event_loop(wlLoopSource->display), 0); + return G_SOURCE_CONTINUE; + }, + nullptr, // finalize + nullptr, // closure_callback + nullptr, // closure_marshall +}; + +static GRefPtr<GSource> createWaylandLoopSource(struct wl_display* display) +{ + GRefPtr<GSource> source = adoptGRef(g_source_new(&waylandLoopSourceFunctions, sizeof(WaylandLoopSource))); + g_source_set_name(source.get(), "Nested Wayland compositor display event source"); + g_source_set_priority(source.get(), G_PRIORITY_DEFAULT + 1); + + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source.get()); + wlLoopSource->display = display; + wlLoopSource->fdTag = g_source_add_unix_fd(source.get(), wl_event_loop_get_fd(wl_display_get_event_loop(display)), static_cast<GIOCondition>(waylandLoopSourceCondition)); + g_source_attach(source.get(), nullptr); + + return source; +} + +WaylandCompositor::WaylandCompositor() +{ + WlUniquePtr<struct wl_display> display(wl_display_create()); + if (!display) { + WTFLogAlways("Nested Wayland compositor could not create display object"); + return; + } + + String displayName = "webkitgtk-wayland-compositor-" + createCanonicalUUIDString(); + if (wl_display_add_socket(display.get(), displayName.utf8().data()) == -1) { + WTFLogAlways("Nested Wayland compositor could not create display socket"); + return; + } + + WlUniquePtr<struct wl_global> compositorGlobal(wl_global_create(display.get(), &wl_compositor_interface, wl_compositor_interface.version, this, + [](struct wl_client* client, void* data, uint32_t version, uint32_t id) { + if (struct wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, std::min(static_cast<int>(version), 3), id)) + wl_resource_set_implementation(resource, &compositorInterface, static_cast<WaylandCompositor*>(data), nullptr); + else + wl_client_post_no_memory(client); + })); + if (!compositorGlobal) { + WTFLogAlways("Nested Wayland compositor could not register compositor global"); + return; + } + + WlUniquePtr<struct wl_global> webkitgtkGlobal(wl_global_create(display.get(), &wl_webkitgtk_interface, 1, this, + [](struct wl_client* client, void* data, uint32_t version, uint32_t id) { + if (struct wl_resource* resource = wl_resource_create(client, &wl_webkitgtk_interface, 1, id)) + wl_resource_set_implementation(resource, &webkitgtkInterface, static_cast<WaylandCompositor*>(data), nullptr); + else + wl_client_post_no_memory(client); + })); + if (!webkitgtkGlobal) { + WTFLogAlways("Nested Wayland compositor could not register webkitgtk global"); + return; + } + + if (!initializeEGL()) { + WTFLogAlways("Nested Wayland compositor could not initialize EGL"); + return; + } + + if (!eglBindWaylandDisplay(PlatformDisplay::sharedDisplay().eglDisplay(), display.get())) { + WTFLogAlways("Nested Wayland compositor could not bind nested display"); + return; + } + + m_displayName = WTFMove(displayName); + m_display = WTFMove(display); + m_compositorGlobal = WTFMove(compositorGlobal); + m_webkitgtkGlobal = WTFMove(webkitgtkGlobal); + m_eventSource = createWaylandLoopSource(m_display.get()); +} + +bool WaylandCompositor::getTexture(WebPageProxy& webPage, unsigned& texture, IntSize& textureSize) +{ + if (auto* surface = m_pageMap.get(&webPage)) + return surface->prepareTextureForPainting(texture, textureSize); + return false; +} + +void WaylandCompositor::bindSurfaceToWebPage(WaylandCompositor::Surface* surface, uint64_t pageID) +{ + WebPageProxy* webPage = nullptr; + for (auto* page : m_pageMap.keys()) { + if (page->pageID() == pageID) { + webPage = page; + break; + } + } + if (!webPage) + return; + + surface->setWebPage(webPage); + m_pageMap.set(webPage, surface); +} + +void WaylandCompositor::registerWebPage(WebPageProxy& webPage) +{ + m_pageMap.add(&webPage, nullptr); +} + +void WaylandCompositor::unregisterWebPage(WebPageProxy& webPage) +{ + if (auto* surface = m_pageMap.take(&webPage)) + surface->setWebPage(nullptr); +} + +} // namespace WebKit + +#endif // PLATFORM(WAYLAND) && USE(EGL) diff --git a/Source/WebKit2/UIProcess/gtk/WaylandCompositor.h b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.h new file mode 100644 index 000000000..14b9a836d --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#if PLATFORM(WAYLAND) && USE(EGL) + +#include "WebPageProxy.h" +#include <WebCore/RefPtrCairo.h> +#include <WebCore/WlUniquePtr.h> +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/Noncopyable.h> +#include <wtf/WeakPtr.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/text/WTFString.h> + +typedef void *EGLImageKHR; + +namespace WebCore { +class GLContext; +} + +namespace WebKit { + +class WebPageProxy; + +class WaylandCompositor { + WTF_MAKE_NONCOPYABLE(WaylandCompositor); + friend class NeverDestroyed<WaylandCompositor>; +public: + static WaylandCompositor& singleton(); + + class Buffer { + WTF_MAKE_NONCOPYABLE(Buffer); WTF_MAKE_FAST_ALLOCATED; + public: + static Buffer* getOrCreate(struct wl_resource*); + ~Buffer(); + + void use(); + void unuse(); + + EGLImageKHR createImage() const; + WebCore::IntSize size() const; + + WeakPtr<Buffer> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } + + private: + Buffer(struct wl_resource*); + static void destroyListenerCallback(struct wl_listener*, void*); + + struct wl_resource* m_resource { nullptr }; + struct wl_listener m_destroyListener; + uint32_t m_busyCount { 0 }; + WeakPtrFactory<Buffer> m_weakPtrFactory; + }; + + class Surface { + WTF_MAKE_NONCOPYABLE(Surface); WTF_MAKE_FAST_ALLOCATED; + public: + Surface(); + ~Surface(); + + void attachBuffer(struct wl_resource*); + void requestFrame(struct wl_resource*); + void commit(); + + void setWebPage(WebPageProxy* webPage) { m_webPage = webPage; } + bool prepareTextureForPainting(unsigned&, WebCore::IntSize&); + + private: + void makePendingBufferCurrent(); + + WeakPtr<Buffer> m_buffer; + WeakPtr<Buffer> m_pendingBuffer; + unsigned m_texture; + EGLImageKHR m_image; + WebCore::IntSize m_imageSize; + Vector<wl_resource*> m_frameCallbackList; + WebPageProxy* m_webPage { nullptr }; + }; + + bool isRunning() const { return !!m_display; } + String displayName() const { return m_displayName; } + + void bindSurfaceToWebPage(Surface*, uint64_t pageID); + void registerWebPage(WebPageProxy&); + void unregisterWebPage(WebPageProxy&); + + bool getTexture(WebPageProxy&, unsigned&, WebCore::IntSize&); + +private: + WaylandCompositor(); + + bool initializeEGL(); + + String m_displayName; + WebCore::WlUniquePtr<struct wl_display> m_display; + WebCore::WlUniquePtr<struct wl_global> m_compositorGlobal; + WebCore::WlUniquePtr<struct wl_global> m_webkitgtkGlobal; + GRefPtr<GSource> m_eventSource; + std::unique_ptr<WebCore::GLContext> m_eglContext; + HashMap<WebPageProxy*, Surface*> m_pageMap; +}; + +} // namespace WebKit + +#endif // PLATFORM(WAYLAND) && USE(EGL) diff --git a/Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.cpp new file mode 100644 index 000000000..32dfe82ec --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS 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 APPLE INC. OR ITS 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. + */ + +#include "config.h" +#include "WebColorPickerGtk.h" + +#if ENABLE(INPUT_TYPE_COLOR) + +#include "WebPageProxy.h" +#include <WebCore/GtkUtilities.h> +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> + +using namespace WebCore; + +namespace WebKit { + +Ref<WebColorPickerGtk> WebColorPickerGtk::create(WebPageProxy& page, const Color& initialColor, const IntRect& rect) +{ + return adoptRef(*new WebColorPickerGtk(page, initialColor, rect)); +} + +WebColorPickerGtk::WebColorPickerGtk(WebPageProxy& page, const Color& initialColor, const IntRect&) + : WebColorPicker(&page) + , m_initialColor(initialColor) + , m_webView(page.viewWidget()) + , m_colorChooser(nullptr) +{ +} + +WebColorPickerGtk::~WebColorPickerGtk() +{ + endPicker(); +} + +void WebColorPickerGtk::cancel() +{ + setSelectedColor(m_initialColor); +} + +void WebColorPickerGtk::endPicker() +{ + if (!m_colorChooser) + return; + + gtk_widget_destroy(m_colorChooser); + m_colorChooser = nullptr; +} + +void WebColorPickerGtk::didChooseColor(const Color& color) +{ + if (!m_client) + return; + + m_client->didChooseColor(color); +} + +void WebColorPickerGtk::colorChooserDialogRGBAChangedCallback(GtkColorChooser* colorChooser, GParamSpec*, WebColorPickerGtk* colorPicker) +{ + GdkRGBA rgba; + gtk_color_chooser_get_rgba(colorChooser, &rgba); + colorPicker->didChooseColor(rgba); +} + +void WebColorPickerGtk::colorChooserDialogResponseCallback(GtkColorChooser*, int responseID, WebColorPickerGtk* colorPicker) +{ + if (responseID != GTK_RESPONSE_OK) + colorPicker->cancel(); + colorPicker->endPicker(); +} + +void WebColorPickerGtk::showColorPicker(const Color& color) +{ + if (!m_client) + return; + + m_initialColor = color; + + if (!m_colorChooser) { + GtkWidget* toplevel = gtk_widget_get_toplevel(m_webView); + m_colorChooser = gtk_color_chooser_dialog_new(_("Select Color"), WebCore::widgetIsOnscreenToplevelWindow(toplevel) ? GTK_WINDOW(toplevel) : nullptr); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(m_colorChooser), &m_initialColor); + g_signal_connect(m_colorChooser, "notify::rgba", G_CALLBACK(WebColorPickerGtk::colorChooserDialogRGBAChangedCallback), this); + g_signal_connect(m_colorChooser, "response", G_CALLBACK(WebColorPickerGtk::colorChooserDialogResponseCallback), this); + } else + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(m_colorChooser), &m_initialColor); + + gtk_widget_show(m_colorChooser); +} + +} // namespace WebKit + +#endif // ENABLE(INPUT_TYPE_COLOR) diff --git a/Source/WebKit2/UIProcess/gtk/WebProcessProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.h index 5907d5657..0bb96a5dd 100644 --- a/Source/WebKit2/UIProcess/gtk/WebProcessProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS 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 APPLE INC. OR ITS CONTRIBUTORS @@ -23,21 +23,51 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "WebProcessProxy.h" -#include <glib.h> +#ifndef WebColorPickerGtk_h +#define WebColorPickerGtk_h -namespace WebKit { +#if ENABLE(INPUT_TYPE_COLOR) + +#include "WebColorPicker.h" +#include <gdk/gdk.h> -void WebProcessProxy::platformGetLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions) -{ -#ifndef NDEBUG - const char* webProcessCmdPrefix = g_getenv("WEB_PROCESS_CMD_PREFIX"); - if (webProcessCmdPrefix && *webProcessCmdPrefix) - launchOptions.processCmdPrefix = String::fromUTF8(webProcessCmdPrefix); -#else - UNUSED_PARAM(launchOptions); -#endif +typedef struct _GtkColorChooser GtkColorChooser; + +namespace WebCore { +class Color; +class IntRect; } +namespace WebKit { + +class WebColorPickerGtk : public WebColorPicker { +public: + static Ref<WebColorPickerGtk> create(WebPageProxy&, const WebCore::Color&, const WebCore::IntRect&); + virtual ~WebColorPickerGtk(); + + void endPicker() override; + void showColorPicker(const WebCore::Color&) override; + + void cancel(); + + const GdkRGBA* initialColor() const { return &m_initialColor; } + +protected: + WebColorPickerGtk(WebPageProxy&, const WebCore::Color&, const WebCore::IntRect&); + + void didChooseColor(const WebCore::Color&); + + GdkRGBA m_initialColor; + GtkWidget* m_webView; + +private: + static void colorChooserDialogRGBAChangedCallback(GtkColorChooser*, GParamSpec*, WebColorPickerGtk*); + static void colorChooserDialogResponseCallback(GtkColorChooser*, int /*responseID*/, WebColorPickerGtk*); + + GtkWidget* m_colorChooser; +}; + } // namespace WebKit + +#endif // ENABLE(INPUT_TYPE_COLOR) +#endif // WebColorPickerGtk_h diff --git a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp index cc25e644b..5c6a8a08e 100644 --- a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp @@ -28,152 +28,180 @@ #if ENABLE(CONTEXT_MENUS) +#include "APIContextMenuClient.h" #include "NativeWebMouseEvent.h" +#include "WebContextMenuItem.h" #include "WebContextMenuItemData.h" #include "WebKitWebViewBasePrivate.h" #include "WebPageProxy.h" +#include "WebProcessProxy.h" #include <WebCore/GtkUtilities.h> +#include <gio/gio.h> #include <gtk/gtk.h> #include <wtf/text/CString.h> - static const char* gContextMenuActionId = "webkit-context-menu-action"; +static const char* gContextMenuTitle = "webkit-context-menu-title"; +static const char* gContextMenuItemGroup = "webkitContextMenu"; using namespace WebCore; namespace WebKit { -static void contextMenuItemActivatedCallback(GtkAction* action, WebPageProxy* page) +static void contextMenuItemActivatedCallback(GAction* action, GVariant*, WebPageProxy* page) { - gboolean isToggle = GTK_IS_TOGGLE_ACTION(action); - WebKit::WebContextMenuItemData item(isToggle ? WebCore::CheckableActionType : WebCore::ActionType, - static_cast<WebCore::ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), gContextMenuActionId))), - String::fromUTF8(gtk_action_get_label(action)), gtk_action_get_sensitive(action), - isToggle ? gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)) : false); + auto* stateType = g_action_get_state_type(action); + gboolean isToggle = stateType && g_variant_type_equal(stateType, G_VARIANT_TYPE_BOOLEAN); + GRefPtr<GVariant> state = isToggle ? adoptGRef(g_action_get_state(action)) : nullptr; + WebContextMenuItemData item(isToggle ? CheckableActionType : ActionType, + static_cast<ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), gContextMenuActionId))), + String::fromUTF8(static_cast<const char*>(g_object_get_data(G_OBJECT(action), gContextMenuTitle))), g_action_get_enabled(action), + state ? g_variant_get_boolean(state.get()) : false); page->contextMenuItemSelected(item); } -static void contextMenuItemVisibilityChanged(GtkAction* action, GParamSpec*, WebContextMenuProxyGtk* contextMenuProxy) +void WebContextMenuProxyGtk::append(GMenu* menu, const WebContextMenuItemGtk& menuItem) { - GtkMenu* menu = contextMenuProxy->gtkMenu(); - if (!menu) - return; - - GUniquePtr<GList> items(gtk_container_get_children(GTK_CONTAINER(menu))); - bool previousVisibleItemIsNotASeparator = false; - GtkWidget* lastItemVisibleSeparator = 0; - for (GList* iter = items.get(); iter; iter = g_list_next(iter)) { - GtkWidget* widget = GTK_WIDGET(iter->data); - - if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) { - if (previousVisibleItemIsNotASeparator) { - gtk_widget_show(widget); - lastItemVisibleSeparator = widget; - previousVisibleItemIsNotASeparator = false; - } else - gtk_widget_hide(widget); - } else if (gtk_widget_get_visible(widget)) { - lastItemVisibleSeparator = 0; - previousVisibleItemIsNotASeparator = true; - } + unsigned long signalHandlerId; + GRefPtr<GMenuItem> gMenuItem; + GAction* action = menuItem.gAction(); + ASSERT(action); +#if GTK_CHECK_VERSION(3, 16, 0) + g_action_map_add_action(G_ACTION_MAP(gtk_widget_get_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup)), action); +#else + g_action_map_add_action(G_ACTION_MAP(g_object_get_data(G_OBJECT(m_menu), gContextMenuItemGroup)), action); +#endif + + switch (menuItem.type()) { + case ActionType: + case CheckableActionType: { + GUniquePtr<char> actionName(g_strdup_printf("%s.%s", gContextMenuItemGroup, g_action_get_name(action))); + gMenuItem = adoptGRef(g_menu_item_new(menuItem.title().utf8().data(), nullptr)); + g_menu_item_set_action_and_target_value(gMenuItem.get(), actionName.get(), nullptr); + + g_object_set_data(G_OBJECT(action), gContextMenuActionId, GINT_TO_POINTER(menuItem.action())); + g_object_set_data_full(G_OBJECT(action), gContextMenuTitle, g_strdup(menuItem.title().utf8().data()), g_free); + signalHandlerId = g_signal_connect(action, "activate", G_CALLBACK(contextMenuItemActivatedCallback), m_page); + m_signalHandlers.set(signalHandlerId, action); + break; + } + case SubmenuType: { + GRefPtr<GMenu> submenu = buildMenu(menuItem.submenuItems()); + gMenuItem = adoptGRef(g_menu_item_new_submenu(menuItem.title().utf8().data(), G_MENU_MODEL(submenu.get()))); + break; + } + case SeparatorType: + ASSERT_NOT_REACHED(); + break; } - if (lastItemVisibleSeparator) - gtk_widget_hide(lastItemVisibleSeparator); + g_menu_append_item(menu, gMenuItem.get()); } -void WebContextMenuProxyGtk::append(ContextMenuItem& menuItem) +GRefPtr<GMenu> WebContextMenuProxyGtk::buildMenu(const Vector<WebContextMenuItemGtk>& items) { - unsigned long signalHandlerId; - GtkAction* action = menuItem.gtkAction(); - if (action) { - switch (menuItem.type()) { - case ActionType: - case CheckableActionType: - g_object_set_data(G_OBJECT(action), gContextMenuActionId, GINT_TO_POINTER(menuItem.action())); - signalHandlerId = g_signal_connect(action, "activate", G_CALLBACK(contextMenuItemActivatedCallback), m_page); - m_signalHandlers.set(signalHandlerId, action); - // Fall through. - case SubmenuType: - signalHandlerId = g_signal_connect(action, "notify::visible", G_CALLBACK(contextMenuItemVisibilityChanged), this); - m_signalHandlers.set(signalHandlerId, action); - break; - case SeparatorType: - break; - } + GRefPtr<GMenu> menu = adoptGRef(g_menu_new()); + GMenu* sectionMenu = menu.get(); + for (const auto& item : items) { + if (item.type() == SeparatorType) { + GRefPtr<GMenu> section = adoptGRef(g_menu_new()); + g_menu_append_section(menu.get(), nullptr, G_MENU_MODEL(section.get())); + sectionMenu = section.get(); + } else + append(sectionMenu, item); } - m_menu.appendItem(menuItem); + return menu; } -// Populate the context menu ensuring that: -// - There aren't separators next to each other. -// - There aren't separators at the beginning of the menu. -// - There aren't separators at the end of the menu. -void WebContextMenuProxyGtk::populate(Vector<ContextMenuItem>& items) +void WebContextMenuProxyGtk::populate(const Vector<WebContextMenuItemGtk>& items) { - bool previousIsSeparator = false; - bool isEmpty = true; - for (size_t i = 0; i < items.size(); i++) { - ContextMenuItem& menuItem = items.at(i); - if (menuItem.type() == SeparatorType) { - previousIsSeparator = true; - continue; - } - - if (previousIsSeparator && !isEmpty) - append(items.at(i - 1)); - previousIsSeparator = false; - - append(menuItem); - isEmpty = false; - } + GRefPtr<GMenu> menu = buildMenu(items); + gtk_menu_shell_bind_model(GTK_MENU_SHELL(m_menu), G_MENU_MODEL(menu.get()), nullptr, TRUE); } -void WebContextMenuProxyGtk::populate(const Vector<WebContextMenuItemData>& items) +void WebContextMenuProxyGtk::populate(const Vector<RefPtr<WebContextMenuItem>>& items) { - for (size_t i = 0; i < items.size(); i++) { - ContextMenuItem menuitem = items.at(i).core(); - append(menuitem); + GRefPtr<GMenu> menu = adoptGRef(g_menu_new()); + GMenu* sectionMenu = menu.get(); + for (const auto& item : items) { + if (item->data().type() == SeparatorType) { + GRefPtr<GMenu> section = adoptGRef(g_menu_new()); + g_menu_append_section(menu.get(), nullptr, G_MENU_MODEL(section.get())); + sectionMenu = section.get(); + } else { + WebContextMenuItemGtk menuitem(item->data()); + append(sectionMenu, menuitem); + } } + gtk_menu_shell_bind_model(GTK_MENU_SHELL(m_menu), G_MENU_MODEL(menu.get()), nullptr, TRUE); } -void WebContextMenuProxyGtk::showContextMenu(const WebCore::IntPoint& position, const Vector<WebContextMenuItemData>& items) +void WebContextMenuProxyGtk::show() { + Vector<RefPtr<WebContextMenuItem>> proposedAPIItems; + for (auto& item : m_context.menuItems()) { + if (item.action() != ContextMenuItemTagShareMenu) + proposedAPIItems.append(WebContextMenuItem::create(item)); + } + + Vector<RefPtr<WebContextMenuItem>> clientItems; + bool useProposedItems = true; + + if (m_page->contextMenuClient().getContextMenuFromProposedMenu(*m_page, proposedAPIItems, clientItems, m_context.webHitTestResultData(), m_page->process().transformHandlesToObjects(m_userData.object()).get())) + useProposedItems = false; + + const Vector<RefPtr<WebContextMenuItem>>& items = useProposedItems ? proposedAPIItems : clientItems; + if (!items.isEmpty()) populate(items); - if (!m_menu.itemCount()) + unsigned childCount = 0; + gtk_container_foreach(GTK_CONTAINER(m_menu), [](GtkWidget*, gpointer data) { (*static_cast<unsigned*>(data))++; }, &childCount); + if (!childCount) return; - m_popupPosition = convertWidgetPointToScreenPoint(m_webView, position); + m_popupPosition = convertWidgetPointToScreenPoint(m_webView, m_context.menuLocation()); // Display menu initiated by right click (mouse button pressed = 3). NativeWebMouseEvent* mouseEvent = m_page->currentlyProcessedMouseDownEvent(); const GdkEvent* event = mouseEvent ? mouseEvent->nativeEvent() : 0; - gtk_menu_attach_to_widget(m_menu.platformDescription(), GTK_WIDGET(m_webView), 0); - gtk_menu_popup(m_menu.platformDescription(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, + gtk_menu_attach_to_widget(m_menu, GTK_WIDGET(m_webView), nullptr); + gtk_menu_popup(m_menu, nullptr, nullptr, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, event ? event->button.button : 3, event ? event->button.time : GDK_CURRENT_TIME); } -void WebContextMenuProxyGtk::hideContextMenu() +void WebContextMenuProxyGtk::showContextMenuWithItems(const Vector<WebContextMenuItemData>& items) { - gtk_menu_popdown(m_menu.platformDescription()); } -WebContextMenuProxyGtk::WebContextMenuProxyGtk(GtkWidget* webView, WebPageProxy* page) - : m_webView(webView) - , m_page(page) +WebContextMenuProxyGtk::WebContextMenuProxyGtk(GtkWidget* webView, WebPageProxy& page, const ContextMenuContextData& context, const UserData& userData) + : WebContextMenuProxy(context, userData) + , m_webView(webView) + , m_page(&page) + , m_menu(GTK_MENU(gtk_menu_new())) { + GRefPtr<GSimpleActionGroup> group = adoptGRef(g_simple_action_group_new()); + gtk_widget_insert_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup, G_ACTION_GROUP(group.get())); +#if !GTK_CHECK_VERSION(3, 16, 0) + g_object_set_data(G_OBJECT(m_menu), gContextMenuItemGroup, group.get()); +#endif webkitWebViewBaseSetActiveContextMenuProxy(WEBKIT_WEB_VIEW_BASE(m_webView), this); } WebContextMenuProxyGtk::~WebContextMenuProxyGtk() { - for (auto iter = m_signalHandlers.begin(); iter != m_signalHandlers.end(); ++iter) - g_signal_handler_disconnect(iter->value, iter->key); + gtk_menu_popdown(m_menu); + + for (auto& handler : m_signalHandlers) + g_signal_handler_disconnect(handler.value, handler.key); + m_signalHandlers.clear(); - webkitWebViewBaseSetActiveContextMenuProxy(WEBKIT_WEB_VIEW_BASE(m_webView), 0); + gtk_widget_insert_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup, nullptr); +#if !GTK_CHECK_VERSION(3, 16, 0) + g_object_set_data(G_OBJECT(m_menu), gContextMenuItemGroup, nullptr); +#endif + gtk_widget_destroy(GTK_WIDGET(m_menu)); } void WebContextMenuProxyGtk::menuPositionFunction(GtkMenu* menu, gint* x, gint* y, gboolean* pushIn, WebContextMenuProxyGtk* popupMenu) diff --git a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.h b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.h index ded4b7a84..144be9829 100644 --- a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.h +++ b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.h @@ -28,42 +28,41 @@ #if ENABLE(CONTEXT_MENUS) +#include "WebContextMenuItemGtk.h" #include "WebContextMenuProxy.h" -#include <WebCore/ContextMenu.h> #include <WebCore/IntPoint.h> #include <wtf/HashMap.h> +#include <wtf/glib/GRefPtr.h> + +typedef struct _GMenu GMenu; namespace WebKit { +class WebContextMenuItem; class WebContextMenuItemData; class WebPageProxy; class WebContextMenuProxyGtk : public WebContextMenuProxy { public: - static PassRefPtr<WebContextMenuProxyGtk> create(GtkWidget* webView, WebPageProxy* page) - { - return adoptRef(new WebContextMenuProxyGtk(webView, page)); - } + WebContextMenuProxyGtk(GtkWidget*, WebPageProxy&, const ContextMenuContextData&, const UserData&); ~WebContextMenuProxyGtk(); - virtual void showContextMenu(const WebCore::IntPoint&, const Vector<WebContextMenuItemData>&); - virtual void hideContextMenu(); - - void populate(Vector<WebCore::ContextMenuItem>&); - GtkMenu* gtkMenu() const { return m_menu.platformDescription(); } + void populate(const Vector<WebContextMenuItemGtk>&); + GtkMenu* gtkMenu() const { return m_menu; } private: - WebContextMenuProxyGtk(GtkWidget*, WebPageProxy*); - - void append(WebCore::ContextMenuItem&); - void populate(const Vector<WebContextMenuItemData>&); + void show() override; + void showContextMenuWithItems(const Vector<WebContextMenuItemData>&) override; + void append(GMenu*, const WebContextMenuItemGtk&); + GRefPtr<GMenu> buildMenu(const Vector<WebContextMenuItemGtk>&); + void populate(const Vector<RefPtr<WebContextMenuItem>>&); static void menuPositionFunction(GtkMenu*, gint*, gint*, gboolean*, WebContextMenuProxyGtk*); GtkWidget* m_webView; WebPageProxy* m_page; - WebCore::ContextMenu m_menu; + GtkMenu* m_menu; WebCore::IntPoint m_popupPosition; - HashMap<unsigned long, GtkAction*> m_signalHandlers; + HashMap<unsigned long, void*> m_signalHandlers; }; diff --git a/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.cpp index 5884c32ad..f42473345 100644 --- a/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.cpp @@ -27,7 +27,7 @@ #include "WebInspectorClientGtk.h" #include "WKAPICast.h" -#include "WKSharedAPICast.h" +#include "WebInspectorProxy.h" #include <wtf/text/WTFString.h> namespace WebKit { @@ -88,4 +88,11 @@ void WebInspectorClientGtk::didChangeAttachedWidth(WebInspectorProxy* inspector, m_client.didChangeAttachedWidth(toAPI(inspector), width, m_client.base.clientInfo); } +void WebInspectorClientGtk::didChangeAttachAvailability(WebInspectorProxy* inspector, bool available) +{ + if (!m_client.didChangeAttachAvailability) + return; + m_client.didChangeAttachAvailability(toAPI(inspector), available, m_client.base.clientInfo); +} + } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.h b/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.h index 9d7b0eb68..2d4f6193f 100644 --- a/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.h +++ b/Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.h @@ -23,12 +23,10 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebInspectorClientGtk_h -#define WebInspectorClientGtk_h +#pragma once #include "APIClient.h" #include "WKInspectorClientGtk.h" - #include <wtf/text/WTFString.h> namespace API { @@ -53,8 +51,7 @@ public: bool detach(WebInspectorProxy*); void didChangeAttachedHeight(WebInspectorProxy*, unsigned height); void didChangeAttachedWidth(WebInspectorProxy*, unsigned width); + void didChangeAttachAvailability(WebInspectorProxy*, bool available); }; } // namespace WebKit - -#endif // WebInspectorClientGtk_h diff --git a/Source/WebKit2/UIProcess/gtk/WebInspectorProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebInspectorProxyGtk.cpp index 231bc4ffb..b46d114a4 100644 --- a/Source/WebKit2/UIProcess/gtk/WebInspectorProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebInspectorProxyGtk.cpp @@ -29,16 +29,16 @@ #include "config.h" #include "WebInspectorProxy.h" -#if ENABLE(INSPECTOR) - #include "WebKitWebViewBasePrivate.h" +#include "WebPageGroup.h" +#include "WebProcessPool.h" #include "WebProcessProxy.h" #include <WebCore/FileSystem.h> #include <WebCore/GtkUtilities.h> #include <WebCore/NotImplemented.h> #include <glib/gi18n-lib.h> #include <gtk/gtk.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> @@ -53,6 +53,11 @@ static void inspectorViewDestroyed(GtkWidget*, gpointer userData) inspectorProxy->close(); } +static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long currentDatabaseUsage, unsigned long long expectedUsage, const void*) +{ + return std::max<unsigned long long>(expectedUsage, currentDatabaseUsage * 1.25); +} + void WebInspectorProxy::initializeInspectorClientGtk(const WKInspectorClientGtkBase* inspectorClient) { m_client.initialize(inspectorClient); @@ -60,11 +65,81 @@ void WebInspectorProxy::initializeInspectorClientGtk(const WKInspectorClientGtkB WebPageProxy* WebInspectorProxy::platformCreateInspectorPage() { - ASSERT(m_page); + ASSERT(inspectedPage()); ASSERT(!m_inspectorView); - m_inspectorView = GTK_WIDGET(webkitWebViewBaseCreate(&page()->process().context(), inspectorPageGroup(), m_page)); + + RefPtr<WebPreferences> preferences = WebPreferences::create(String(), "WebKit2.", "WebKit2."); +#if ENABLE(DEVELOPER_MODE) + // Allow developers to inspect the Web Inspector in debug builds without changing settings. + preferences->setDeveloperExtrasEnabled(true); + preferences->setLogsPageMessagesToSystemConsoleEnabled(true); +#endif + preferences->setJavaScriptRuntimeFlags({ + }); + RefPtr<WebPageGroup> pageGroup = WebPageGroup::create(inspectorPageGroupIdentifierForPage(inspectedPage()), false, false); + + auto pageConfiguration = API::PageConfiguration::create(); + pageConfiguration->setProcessPool(&inspectorProcessPool(inspectionLevel())); + pageConfiguration->setPreferences(preferences.get()); + pageConfiguration->setPageGroup(pageGroup.get()); + m_inspectorView = GTK_WIDGET(webkitWebViewBaseCreate(*pageConfiguration.ptr())); g_object_add_weak_pointer(G_OBJECT(m_inspectorView), reinterpret_cast<void**>(&m_inspectorView)); - return webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(m_inspectorView)); + + WKPageUIClientV2 uiClient = { + { 2, this }, + nullptr, // createNewPage_deprecatedForUseWithV0 + nullptr, // showPage + nullptr, // closePage + nullptr, // takeFocus + nullptr, // focus + nullptr, // unfocus + nullptr, // runJavaScriptAlert + nullptr, // runJavaScriptConfirm + nullptr, // runJavaScriptPrompt + nullptr, // setStatusText + nullptr, // mouseDidMoveOverElement_deprecatedForUseWithV0 + nullptr, // missingPluginButtonClicked_deprecatedForUseWithV0 + nullptr, // didNotHandleKeyEvent + nullptr, // didNotHandleWheelEvent + nullptr, // areToolbarsVisible + nullptr, // setToolbarsVisible + nullptr, // isMenuBarVisible + nullptr, // setMenuBarVisible + nullptr, // isStatusBarVisible + nullptr, // setStatusBarVisible + nullptr, // isResizable + nullptr, // setResizable + nullptr, // getWindowFrame, + nullptr, // setWindowFrame, + nullptr, // runBeforeUnloadConfirmPanel + nullptr, // didDraw + nullptr, // pageDidScroll + exceededDatabaseQuota, + nullptr, // runOpenPanel, + nullptr, // decidePolicyForGeolocationPermissionRequest + nullptr, // headerHeight + nullptr, // footerHeight + nullptr, // drawHeader + nullptr, // drawFooter + nullptr, // printFrame + nullptr, // runModal + nullptr, // unused + nullptr, // saveDataToFileInDownloadsFolder + nullptr, // shouldInterruptJavaScript + nullptr, // createPage + nullptr, // mouseDidMoveOverElement + nullptr, // decidePolicyForNotificationPermissionRequest + nullptr, // unavailablePluginButtonClicked_deprecatedForUseWithV1 + nullptr, // showColorPicker + nullptr, // hideColorPicker + nullptr, // unavailablePluginButtonClicked + }; + + WebPageProxy* inspectorPage = webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(m_inspectorView)); + ASSERT(inspectorPage); + WKPageSetPageUIClient(toAPI(inspectorPage), &uiClient.base); + + return inspectorPage; } void WebInspectorProxy::createInspectorWindow() @@ -75,11 +150,18 @@ void WebInspectorProxy::createInspectorWindow() ASSERT(!m_inspectorWindow); m_inspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); - GtkWidget* inspectedViewParent = gtk_widget_get_toplevel(m_page->viewWidget()); + GtkWidget* inspectedViewParent = gtk_widget_get_toplevel(inspectedPage()->viewWidget()); if (WebCore::widgetIsOnscreenToplevelWindow(inspectedViewParent)) gtk_window_set_transient_for(GTK_WINDOW(m_inspectorWindow), GTK_WINDOW(inspectedViewParent)); - gtk_window_set_title(GTK_WINDOW(m_inspectorWindow), _("Web Inspector")); +#if GTK_CHECK_VERSION(3, 10, 0) + m_headerBar = gtk_header_bar_new(); + gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(m_headerBar), TRUE); + gtk_window_set_titlebar(GTK_WINDOW(m_inspectorWindow), m_headerBar); + gtk_widget_show(m_headerBar); +#endif + + updateInspectorWindowTitle(); gtk_window_set_default_size(GTK_WINDOW(m_inspectorWindow), initialWindowWidth, initialWindowHeight); gtk_container_add(GTK_CONTAINER(m_inspectorWindow), m_inspectorView); @@ -89,6 +171,23 @@ void WebInspectorProxy::createInspectorWindow() gtk_window_present(GTK_WINDOW(m_inspectorWindow)); } +void WebInspectorProxy::updateInspectorWindowTitle() const +{ + ASSERT(m_inspectorWindow); + if (m_inspectedURLString.isEmpty()) { + gtk_window_set_title(GTK_WINDOW(m_inspectorWindow), _("Web Inspector")); + return; + } + +#if GTK_CHECK_VERSION(3, 10, 0) + gtk_header_bar_set_title(GTK_HEADER_BAR(m_headerBar), _("Web Inspector")); + gtk_header_bar_set_subtitle(GTK_HEADER_BAR(m_headerBar), m_inspectedURLString.utf8().data()); +#else + GUniquePtr<gchar> title(g_strdup_printf("%s - %s", _("Web Inspector"), m_inspectedURLString.utf8().data())); + gtk_window_set_title(GTK_WINDOW(m_inspectorWindow), title.get()); +#endif +} + void WebInspectorProxy::platformOpen() { ASSERT(!m_inspectorWindow); @@ -115,6 +214,14 @@ void WebInspectorProxy::platformDidClose() m_inspectorView = 0; } +void WebInspectorProxy::platformDidCloseForCrash() +{ +} + +void WebInspectorProxy::platformInvalidate() +{ +} + void WebInspectorProxy::platformHide() { notImplemented(); @@ -130,6 +237,11 @@ void WebInspectorProxy::platformBringToFront() gtk_window_present(GTK_WINDOW(parent)); } +void WebInspectorProxy::platformBringInspectedPageToFront() +{ + notImplemented(); +} + bool WebInspectorProxy::platformIsFront() { GtkWidget* parent = gtk_widget_get_toplevel(m_inspectorView); @@ -140,32 +252,36 @@ bool WebInspectorProxy::platformIsFront() void WebInspectorProxy::platformInspectedURLChanged(const String& url) { + m_inspectedURLString = url; m_client.inspectedURLChanged(this, url); - if (!m_inspectorWindow) - return; - GUniquePtr<gchar> title(g_strdup_printf("%s - %s", _("Web Inspector"), url.utf8().data())); - gtk_window_set_title(GTK_WINDOW(m_inspectorWindow), title.get()); + if (m_inspectorWindow) + updateInspectorWindowTitle(); } -String WebInspectorProxy::inspectorPageURL() const +String WebInspectorProxy::inspectorPageURL() { return String("resource:///org/webkitgtk/inspector/UserInterface/Main.html"); } -String WebInspectorProxy::inspectorBaseURL() const +String WebInspectorProxy::inspectorTestPageURL() +{ + return String("resource:///org/webkitgtk/inspector/UserInterface/Test.html"); +} + +String WebInspectorProxy::inspectorBaseURL() { return String("resource:///org/webkitgtk/inspector/UserInterface/"); } unsigned WebInspectorProxy::platformInspectedWindowHeight() { - return gtk_widget_get_allocated_height(m_page->viewWidget()); + return gtk_widget_get_allocated_height(inspectedPage()->viewWidget()); } unsigned WebInspectorProxy::platformInspectedWindowWidth() { - return gtk_widget_get_allocated_width(m_page->viewWidget()); + return gtk_widget_get_allocated_width(inspectedPage()->viewWidget()); } void WebInspectorProxy::platformAttach() @@ -177,9 +293,12 @@ void WebInspectorProxy::platformAttach() m_inspectorWindow = 0; } - // Set a default attached size based on InspectorFrontendClientLocal. + // Set a default sizes based on InspectorFrontendClientLocal. static const unsigned defaultAttachedSize = 300; - if (m_attachmentSide == AttachmentSideBottom) { + static const unsigned minimumAttachedWidth = 750; + static const unsigned minimumAttachedHeight = 250; + + if (m_attachmentSide == AttachmentSide::Bottom) { unsigned maximumAttachedHeight = platformInspectedWindowHeight() * 3 / 4; platformSetAttachedWindowHeight(std::max(minimumAttachedHeight, std::min(defaultAttachedSize, maximumAttachedHeight))); } else { @@ -190,24 +309,33 @@ void WebInspectorProxy::platformAttach() if (m_client.attach(this)) return; - webkitWebViewBaseAddWebInspector(WEBKIT_WEB_VIEW_BASE(m_page->viewWidget()), m_inspectorView, m_attachmentSide); + webkitWebViewBaseAddWebInspector(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), m_inspectorView, m_attachmentSide); gtk_widget_show(m_inspectorView); } void WebInspectorProxy::platformDetach() { - if (!m_page->isValid()) + if (!inspectedPage()->isValid()) return; GRefPtr<GtkWidget> inspectorView = m_inspectorView; if (!m_client.detach(this)) { - GtkWidget* parent = gtk_widget_get_parent(m_inspectorView); - ASSERT(parent); - gtk_container_remove(GTK_CONTAINER(parent), m_inspectorView); + // Detach is called when m_isAttached is true, but it could called before + // the inspector is opened if the inspector is shown/closed quickly. So, + // we might not have a parent yet. + if (GtkWidget* parent = gtk_widget_get_parent(m_inspectorView)) + gtk_container_remove(GTK_CONTAINER(parent), m_inspectorView); } - if (!m_isVisible) + // Return early if we are not visible. This means the inspector was closed while attached + // and we should not create and show the inspector window. + if (!m_isVisible) { + // The inspector view will be destroyed, but we don't need to notify the web process to close the + // inspector in this case, since it's already closed. + g_signal_handlers_disconnect_by_func(m_inspectorView, reinterpret_cast<void*>(inspectorViewDestroyed), this); + m_inspectorView = nullptr; return; + } createInspectorWindow(); } @@ -218,7 +346,7 @@ void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned height) return; m_client.didChangeAttachedHeight(this, height); - webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(m_page->viewWidget()), height); + webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), height); } void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned width) @@ -227,10 +355,10 @@ void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned width) return; m_client.didChangeAttachedWidth(this, width); - webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(m_page->viewWidget()), width); + webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), width); } -void WebInspectorProxy::platformSetToolbarHeight(unsigned) +void WebInspectorProxy::platformStartWindowDrag() { notImplemented(); } @@ -245,11 +373,9 @@ void WebInspectorProxy::platformAppend(const String&, const String&) notImplemented(); } -void WebInspectorProxy::platformAttachAvailabilityChanged(bool) +void WebInspectorProxy::platformAttachAvailabilityChanged(bool available) { - notImplemented(); + m_client.didChangeAttachAvailability(this, available); } } // namespace WebKit - -#endif // ENABLE(INSPECTOR) diff --git a/Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp index ac5ba7352..ff8b4aa0b 100644 --- a/Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp @@ -27,14 +27,17 @@ #include "config.h" #include "WebPageProxy.h" -#include "NativeWebKeyboardEvent.h" #include "NotImplemented.h" #include "PageClientImpl.h" #include "WebKitWebViewBasePrivate.h" #include "WebPageMessages.h" +#include "WebPasteboardProxy.h" #include "WebProcessProxy.h" -#include <WebCore/UserAgentGtk.h> +#include "WebsiteDataStore.h" +#include <WebCore/PlatformDisplay.h> +#include <WebCore/UserAgent.h> #include <gtk/gtkx.h> +#include <wtf/NeverDestroyed.h> namespace WebKit { @@ -52,34 +55,42 @@ String WebPageProxy::standardUserAgent(const String& applicationNameForUserAgent return WebCore::standardUserAgent(applicationNameForUserAgent); } -void WebPageProxy::getEditorCommandsForKeyEvent(const AtomicString& eventType, Vector<WTF::String>& commandsList) +void WebPageProxy::bindAccessibilityTree(const String& plugID) { - // When the keyboard event is started in the WebProcess side (e.g. from the Inspector) - // it will arrive without a GdkEvent associated, so the keyEventQueue will be empty. - if (!m_keyEventQueue.isEmpty()) - m_pageClient.getEditorCommandsForKeyEvent(m_keyEventQueue.first(), eventType, commandsList); + m_accessibilityPlugID = plugID; } -void WebPageProxy::bindAccessibilityTree(const String& plugID) +void WebPageProxy::saveRecentSearches(const String&, const Vector<WebCore::RecentSearch>&) { - m_accessibilityPlugID = plugID; + notImplemented(); } -void WebPageProxy::saveRecentSearches(const String&, const Vector<String>&) +void WebPageProxy::loadRecentSearches(const String&, Vector<WebCore::RecentSearch>&) { notImplemented(); } -void WebPageProxy::loadRecentSearches(const String&, Vector<String>&) +void WebsiteDataStore::platformRemoveRecentSearches(std::chrono::system_clock::time_point oldestTimeToRemove) { notImplemented(); } +void WebPageProxy::editorStateChanged(const EditorState& editorState) +{ + m_editorState = editorState; + + if (editorState.shouldIgnoreCompositionSelectionChange) + return; + if (m_editorState.selectionIsRange) + WebPasteboardProxy::singleton().setPrimarySelectionOwner(focusedFrame()); + m_pageClient.selectionDidChange(); +} + #if PLUGIN_ARCHITECTURE(X11) typedef HashMap<uint64_t, GtkWidget* > PluginWindowMap; static PluginWindowMap& pluginWindowMap() { - DEFINE_STATIC_LOCAL(PluginWindowMap, map, ()); + static NeverDestroyed<PluginWindowMap> map; return map; } @@ -92,6 +103,7 @@ static gboolean pluginContainerPlugRemoved(GtkSocket* socket) void WebPageProxy::createPluginContainer(uint64_t& windowID) { + RELEASE_ASSERT(WebCore::PlatformDisplay::sharedDisplay().type() == WebCore::PlatformDisplay::Type::X11); GtkWidget* socket = gtk_socket_new(); g_signal_connect(socket, "plug-removed", G_CALLBACK(pluginContainerPlugRemoved), 0); gtk_container_add(GTK_CONTAINER(viewWidget()), socket); @@ -134,10 +146,10 @@ void WebPageProxy::setInputMethodState(bool enabled) webkitWebViewBaseSetInputMethodState(WEBKIT_WEB_VIEW_BASE(viewWidget()), enabled); } -#if USE(TEXTURE_MAPPER_GL) -void WebPageProxy::setAcceleratedCompositingWindowId(uint64_t nativeWindowId) +#if HAVE(GTK_GESTURES) +void WebPageProxy::getCenterForZoomGesture(const WebCore::IntPoint& centerInViewCoordinates, WebCore::IntPoint& center) { - process().send(Messages::WebPage::SetAcceleratedCompositingWindowId(nativeWindowId), m_pageID); + process().sendSync(Messages::WebPage::GetCenterForZoomGesture(centerInViewCoordinates), Messages::WebPage::GetCenterForZoomGesture::Reply(center), m_pageID); } #endif diff --git a/Source/WebKit2/UIProcess/gtk/WebPasteboardProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebPasteboardProxyGtk.cpp new file mode 100644 index 000000000..1b15869bd --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WebPasteboardProxyGtk.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``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 APPLE INC. OR ITS 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. + */ + +#include "config.h" +#include "WebPasteboardProxy.h" + +#include "WebFrameProxy.h" +#include "WebSelectionData.h" +#include <WebCore/PlatformPasteboard.h> +#include <wtf/SetForScope.h> + +using namespace WebCore; + +namespace WebKit { + +void WebPasteboardProxy::writeToClipboard(const String& pasteboardName, const WebSelectionData& selection) +{ + SetForScope<WebFrameProxy*> frameWritingToClipboard(m_frameWritingToClipboard, m_primarySelectionOwner); + PlatformPasteboard(pasteboardName).writeToClipboard(selection.selectionData, [this] { + if (m_frameWritingToClipboard == m_primarySelectionOwner) + return; + setPrimarySelectionOwner(nullptr); + }); +} + +void WebPasteboardProxy::readFromClipboard(const String& pasteboardName, WebSelectionData& selection) +{ + selection = WebSelectionData(PlatformPasteboard(pasteboardName).readFromClipboard()); +} + +void WebPasteboardProxy::setPrimarySelectionOwner(WebFrameProxy* frame) +{ + if (m_primarySelectionOwner == frame) + return; + + if (m_primarySelectionOwner) + m_primarySelectionOwner->collapseSelection(); + + m_primarySelectionOwner = frame; +} + +void WebPasteboardProxy::didDestroyFrame(WebFrameProxy* frame) +{ + if (frame == m_primarySelectionOwner) + m_primarySelectionOwner = nullptr; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp index 782730de7..2eb0ec488 100644 --- a/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp @@ -29,27 +29,28 @@ #include "NativeWebMouseEvent.h" #include "WebPopupItem.h" #include <WebCore/GtkUtilities.h> +#include <WebCore/IntRect.h> #include <gtk/gtk.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> using namespace WebCore; namespace WebKit { -WebPopupMenuProxyGtk::WebPopupMenuProxyGtk(GtkWidget* webView, WebPopupMenuProxy::Client* client) +WebPopupMenuProxyGtk::WebPopupMenuProxyGtk(GtkWidget* webView, WebPopupMenuProxy::Client& client) : WebPopupMenuProxy(client) , m_webView(webView) - , m_activeItem(-1) + , m_popup(gtk_menu_new()) + , m_dismissMenuTimer(RunLoop::main(), this, &WebPopupMenuProxyGtk::dismissMenuTimerFired) { + g_signal_connect(m_popup, "key-press-event", G_CALLBACK(keyPressEventCallback), this); + g_signal_connect(m_popup, "unmap", G_CALLBACK(menuUnmappedCallback), this); } WebPopupMenuProxyGtk::~WebPopupMenuProxyGtk() { - if (m_popup) { - g_signal_handlers_disconnect_matched(m_popup->platformMenu(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); - hidePopupMenu(); - } + cancelTracking(); } GtkAction* WebPopupMenuProxyGtk::createGtkActionForMenuItem(const WebPopupItem& item, int itemIndex) @@ -63,74 +64,222 @@ GtkAction* WebPopupMenuProxyGtk::createGtkActionForMenuItem(const WebPopupItem& return action; } -void WebPopupMenuProxyGtk::showPopupMenu(const IntRect& rect, TextDirection textDirection, double pageScaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex) +void WebPopupMenuProxyGtk::populatePopupMenu(const Vector<WebPopupItem>& items) { - if (m_popup) - m_popup->clear(); - else - m_popup = GtkPopupMenu::create(); - - const int size = items.size(); - for (int i = 0; i < size; i++) { - if (items[i].m_type == WebPopupItem::Separator) - m_popup->appendSeparator(); - else { - GRefPtr<GtkAction> action = adoptGRef(createGtkActionForMenuItem(items[i], i)); - m_popup->appendItem(action.get()); + int itemIndex = 0; + for (const auto& item : items) { + if (item.m_type == WebPopupItem::Separator) { + GtkWidget* menuItem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), menuItem); + gtk_widget_show(menuItem); + } else { + GRefPtr<GtkAction> action = adoptGRef(createGtkActionForMenuItem(item, itemIndex)); + GtkWidget* menuItem = gtk_action_create_menu_item(action.get()); + gtk_widget_set_tooltip_text(menuItem, gtk_action_get_tooltip(action.get())); + g_signal_connect(menuItem, "select", G_CALLBACK(selectItemCallback), this); + gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), menuItem); + + if (gtk_action_is_visible(action.get())) + gtk_widget_show(menuItem); } + itemIndex++; } +} + +void WebPopupMenuProxyGtk::showPopupMenu(const IntRect& rect, TextDirection, double /* pageScaleFactor */, const Vector<WebPopupItem>& items, const PlatformPopupMenuData&, int32_t selectedIndex) +{ + m_dismissMenuTimer.stop(); + + populatePopupMenu(items); + gtk_menu_set_active(GTK_MENU(m_popup), selectedIndex); + + resetTypeAheadFindState(); IntPoint menuPosition = convertWidgetPointToScreenPoint(m_webView, rect.location()); menuPosition.move(0, rect.height()); - gulong unmapHandler = g_signal_connect(m_popup->platformMenu(), "unmap", G_CALLBACK(menuUnmapped), this); - m_popup->popUp(rect.size(), menuPosition, size, selectedIndex, m_client->currentlyProcessedMouseDownEvent() ? m_client->currentlyProcessedMouseDownEvent()->nativeEvent() : 0); + // This approach follows the one in gtkcombobox.c. + GtkRequisition requisition; + gtk_widget_set_size_request(m_popup, -1, -1); + gtk_widget_get_preferred_size(m_popup, &requisition, nullptr); + gtk_widget_set_size_request(m_popup, std::max(rect.width(), requisition.width), -1); + + if (int itemCount = items.size()) { + GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(m_popup))); + int i; + GList* child; + for (i = 0, child = children.get(); i < itemCount; i++, child = g_list_next(child)) { + if (i > selectedIndex) + break; + + GtkWidget* item = GTK_WIDGET(child->data); + GtkRequisition itemRequisition; + gtk_widget_get_preferred_size(item, &itemRequisition, nullptr); + menuPosition.setY(menuPosition.y() - itemRequisition.height); + } + } else { + // Center vertically the empty popup in the combo box area. + menuPosition.setY(menuPosition.y() - rect.height() / 2); + } + + gtk_menu_attach_to_widget(GTK_MENU(m_popup), GTK_WIDGET(m_webView), nullptr); + + const GdkEvent* event = m_client->currentlyProcessedMouseDownEvent() ? m_client->currentlyProcessedMouseDownEvent()->nativeEvent() : nullptr; + gtk_menu_popup_for_device(GTK_MENU(m_popup), event ? gdk_event_get_device(event) : nullptr, nullptr, nullptr, + [](GtkMenu*, gint* x, gint* y, gboolean* pushIn, gpointer userData) { + // We can pass a pointer to the menuPosition local variable because the nested main loop ensures this is called in the function context. + IntPoint* menuPosition = static_cast<IntPoint*>(userData); + *x = menuPosition->x(); + *y = menuPosition->y(); + *pushIn = menuPosition->y() < 0; + }, &menuPosition, nullptr, event && event->type == GDK_BUTTON_PRESS ? event->button.button : 1, + event ? gdk_event_get_time(event) : GDK_CURRENT_TIME); + + // Now that the menu has a position, schedule a resize to make sure it's resized to fit vertically in the work area. + gtk_widget_queue_resize(m_popup); // PopupMenu can fail to open when there is no mouse grab. // Ensure WebCore does not go into some pesky state. - if (!gtk_widget_get_visible(m_popup->platformMenu())) { + if (!gtk_widget_get_visible(m_popup)) { m_client->failedToShowPopupMenu(); return; } - // WebPageProxy expects the menu to run in a nested run loop, since it invalidates the - // menu right after calling WebPopupMenuProxy::showPopupMenu(). - m_runLoop = adoptGRef(g_main_loop_new(0, FALSE)); - - gdk_threads_leave(); - g_main_loop_run(m_runLoop.get()); - gdk_threads_enter(); - - m_runLoop.clear(); + // This ensures that the active item gets selected after popping up the menu, and + // as it says in "gtkcombobox.c" (line ~1606): it's ugly, but gets the job done. + GtkWidget* activeChild = gtk_menu_get_active(GTK_MENU(m_popup)); + if (activeChild && gtk_widget_get_visible(activeChild)) + gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup), activeChild); +} - g_signal_handler_disconnect(m_popup->platformMenu(), unmapHandler); +void WebPopupMenuProxyGtk::hidePopupMenu() +{ + gtk_menu_popdown(GTK_MENU(m_popup)); + resetTypeAheadFindState(); +} - if (!m_client) +void WebPopupMenuProxyGtk::cancelTracking() +{ + if (!m_popup) return; - m_client->valueChangedForPopupMenu(this, m_activeItem); + m_dismissMenuTimer.stop(); + g_signal_handlers_disconnect_matched(m_popup, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + hidePopupMenu(); + gtk_widget_destroy(m_popup); + m_popup = nullptr; } -void WebPopupMenuProxyGtk::hidePopupMenu() +bool WebPopupMenuProxyGtk::typeAheadFind(GdkEventKey* event) { - m_popup->popDown(); + // If we were given a non-printable character just skip it. + gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval); + if (!g_unichar_isprint(unicodeCharacter)) { + resetTypeAheadFindState(); + return false; + } + + glong charactersWritten; + GUniquePtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, nullptr, &charactersWritten, nullptr)); + if (!utf16String) { + resetTypeAheadFindState(); + return false; + } + + // If the character is the same as the last character, the user is probably trying to + // cycle through the menulist entries. This matches the WebCore behavior for collapsed menulists. + static const uint32_t searchTimeoutMs = 1000; + bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter; + if (event->time - m_previousKeyEventTimestamp > searchTimeoutMs) + m_currentSearchString = String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten); + else if (repeatingCharacter) + m_currentSearchString.append(String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten)); + + m_previousKeyEventTimestamp = event->time; + m_previousKeyEventCharacter = unicodeCharacter; + + GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(m_popup))); + if (!children) + return true; + + // We case fold before searching, because strncmp does not handle non-ASCII characters. + GUniquePtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1)); + size_t prefixLength = strlen(searchStringWithCaseFolded.get()); + + // If a menu item has already been selected, start searching from the current + // item down the list. This will make multiple key presses of the same character + // advance the selection. + GList* currentChild = children.get(); + if (m_currentlySelectedMenuItem) { + currentChild = g_list_find(children.get(), m_currentlySelectedMenuItem); + if (!currentChild) { + m_currentlySelectedMenuItem = nullptr; + currentChild = children.get(); + } + + // Repeating characters should iterate. + if (repeatingCharacter) { + if (GList* nextChild = g_list_next(currentChild)) + currentChild = nextChild; + } + } + + GList* firstChild = currentChild; + do { + currentChild = g_list_next(currentChild); + if (!currentChild) + currentChild = children.get(); + + GUniquePtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1)); + if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) { + gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup), GTK_WIDGET(currentChild->data)); + break; + } + } while (currentChild != firstChild); + + return true; } -void WebPopupMenuProxyGtk::shutdownRunLoop() +void WebPopupMenuProxyGtk::resetTypeAheadFindState() { - if (g_main_loop_is_running(m_runLoop.get())) - g_main_loop_quit(m_runLoop.get()); + m_currentlySelectedMenuItem = nullptr; + m_previousKeyEventCharacter = 0; + m_previousKeyEventTimestamp = 0; + m_currentSearchString = emptyString(); } void WebPopupMenuProxyGtk::menuItemActivated(GtkAction* action, WebPopupMenuProxyGtk* popupMenu) { - popupMenu->setActiveItem(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), "popup-menu-action-index"))); - popupMenu->shutdownRunLoop(); + popupMenu->m_dismissMenuTimer.stop(); + if (popupMenu->m_client) + popupMenu->m_client->valueChangedForPopupMenu(popupMenu, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), "popup-menu-action-index"))); +} + +void WebPopupMenuProxyGtk::dismissMenuTimerFired() +{ + if (m_client) + m_client->valueChangedForPopupMenu(this, -1); +} + +void WebPopupMenuProxyGtk::menuUnmappedCallback(GtkWidget*, WebPopupMenuProxyGtk* popupMenu) +{ + if (!popupMenu->m_client) + return; + + // When an item is activated, the menu is first hidden and then activate signal is emitted, so at this point we don't know + // if the menu has been hidden because an item has been selected or because the menu has been dismissed. Wait until the next + // main loop iteration to dismiss the menu, if an item is activated the timer will be cancelled. + popupMenu->m_dismissMenuTimer.startOneShot(0); +} + +void WebPopupMenuProxyGtk::selectItemCallback(GtkWidget* item, WebPopupMenuProxyGtk* popupMenu) +{ + popupMenu->setCurrentlySelectedMenuItem(item); } -void WebPopupMenuProxyGtk::menuUnmapped(GtkWidget*, WebPopupMenuProxyGtk* popupMenu) +gboolean WebPopupMenuProxyGtk::keyPressEventCallback(GtkWidget*, GdkEventKey* event, WebPopupMenuProxyGtk* popupMenu) { - popupMenu->shutdownRunLoop(); + return popupMenu->typeAheadFind(event); } } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h b/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h index 4b335bcde..c9ceb6988 100644 --- a/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h +++ b/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h @@ -21,11 +21,16 @@ #define WebPopupMenuProxyGtk_h #include "WebPopupMenuProxy.h" -#include <WebCore/GtkPopupMenu.h> -#include <WebCore/IntRect.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/RunLoop.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/text/WTFString.h> typedef struct _GMainLoop GMainLoop; +typedef struct _GdkEventKey GdkEventKey; + +namespace WebCore { +class IntRect; +} namespace WebKit { @@ -33,28 +38,42 @@ class WebPageProxy; class WebPopupMenuProxyGtk : public WebPopupMenuProxy { public: - static PassRefPtr<WebPopupMenuProxyGtk> create(GtkWidget* webView, WebPopupMenuProxy::Client* client) + static Ref<WebPopupMenuProxyGtk> create(GtkWidget* webView, WebPopupMenuProxy::Client& client) { - return adoptRef(new WebPopupMenuProxyGtk(webView, client)); + return adoptRef(*new WebPopupMenuProxyGtk(webView, client)); } ~WebPopupMenuProxyGtk(); - virtual void showPopupMenu(const WebCore::IntRect&, WebCore::TextDirection, double pageScaleFactor, const Vector<WebPopupItem>&, const PlatformPopupMenuData&, int32_t selectedIndex); - virtual void hidePopupMenu(); + void showPopupMenu(const WebCore::IntRect&, WebCore::TextDirection, double pageScaleFactor, const Vector<WebPopupItem>&, const PlatformPopupMenuData&, int32_t selectedIndex) override; + void hidePopupMenu() override; + void cancelTracking() override; private: - WebPopupMenuProxyGtk(GtkWidget*, WebPopupMenuProxy::Client*); - void shutdownRunLoop(); - void setActiveItem(int activeItem) { m_activeItem = activeItem; } + WebPopupMenuProxyGtk(GtkWidget*, WebPopupMenuProxy::Client&); + + void setCurrentlySelectedMenuItem(GtkWidget* item) { m_currentlySelectedMenuItem = item; } GtkAction* createGtkActionForMenuItem(const WebPopupItem&, int itemIndex); + void populatePopupMenu(const Vector<WebPopupItem>&); + void dismissMenuTimerFired(); + + bool typeAheadFind(GdkEventKey*); + void resetTypeAheadFindState(); static void menuItemActivated(GtkAction*, WebPopupMenuProxyGtk*); - static void menuUnmapped(GtkWidget*, WebPopupMenuProxyGtk*); + static void selectItemCallback(GtkWidget*, WebPopupMenuProxyGtk*); + static gboolean keyPressEventCallback(GtkWidget*, GdkEventKey*, WebPopupMenuProxyGtk*); + static void menuUnmappedCallback(GtkWidget*, WebPopupMenuProxyGtk*); + + GtkWidget* m_webView { nullptr }; + GtkWidget* m_popup { nullptr }; + + RunLoop::Timer<WebPopupMenuProxyGtk> m_dismissMenuTimer; - GtkWidget* m_webView; - OwnPtr<WebCore::GtkPopupMenu> m_popup; - int m_activeItem; - GRefPtr<GMainLoop> m_runLoop; + // Typeahead find. + unsigned m_previousKeyEventCharacter { 0 }; + uint32_t m_previousKeyEventTimestamp { 0 }; + GtkWidget* m_currentlySelectedMenuItem { nullptr }; + String m_currentSearchString; }; } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp index 321a9cfcc..eca423b71 100644 --- a/Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp @@ -27,13 +27,17 @@ #include "config.h" #include "WebPreferences.h" +#include "HardwareAccelerationManager.h" #include <WebCore/NotImplemented.h> namespace WebKit { void WebPreferences::platformInitializeStore() { - notImplemented(); + if (!HardwareAccelerationManager::singleton().canUseHardwareAcceleration()) + setAcceleratedCompositingEnabled(false); + else if (HardwareAccelerationManager::singleton().forceHardwareAcceleration()) + setForceCompositingMode(true); } void WebPreferences::platformUpdateStringValueForKey(const String&, const String&) @@ -61,4 +65,28 @@ void WebPreferences::platformUpdateFloatValueForKey(const String&, float) notImplemented(); } +bool WebPreferences::platformGetStringUserValueForKey(const String&, String&) +{ + notImplemented(); + return false; +} + +bool WebPreferences::platformGetBoolUserValueForKey(const String&, bool&) +{ + notImplemented(); + return false; +} + +bool WebPreferences::platformGetUInt32UserValueForKey(const String&, uint32_t&) +{ + notImplemented(); + return false; +} + +bool WebPreferences::platformGetDoubleUserValueForKey(const String&, double&) +{ + notImplemented(); + return false; +} + } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/gtk/WebContextGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebProcessPoolGtk.cpp index fbddf8f67..4daf672e3 100644 --- a/Source/WebKit2/UIProcess/gtk/WebContextGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebProcessPoolGtk.cpp @@ -26,24 +26,20 @@ */ #include "config.h" -#include "WebContext.h" +#include "WebProcessPool.h" +#include "APIProcessPoolConfiguration.h" #include "Logging.h" #include "WebCookieManagerProxy.h" #include "WebInspectorServer.h" #include "WebProcessCreationParameters.h" #include "WebProcessMessages.h" -#include "WebSoupCustomProtocolRequestManager.h" #include <WebCore/FileSystem.h> #include <WebCore/NotImplemented.h> #include <WebCore/SchemeRegistry.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> -#if ENABLE(NETWORK_PROCESS) -#include "NetworkProcessMessages.h" -#endif - namespace WebKit { static void initInspectorServer() @@ -61,7 +57,7 @@ static void initInspectorServer() unsigned short port = 2999; Vector<String> result; - serverAddress.split(":", result); + serverAddress.split(':', result); if (result.size() == 2) { bindAddress = result[0]; @@ -74,7 +70,7 @@ static void initInspectorServer() } else LOG_ERROR("Couldn't parse %s, wrong format? Use 127.0.0.1:2999 instead.", serverAddress.utf8().data()); - if (!WebInspectorServer::shared().listen(bindAddress, port)) + if (!WebInspectorServer::singleton().listen(bindAddress, port)) LOG_ERROR("Couldn't start listening on: IP address=%s, port=%d.", bindAddress.utf8().data(), port); return; } @@ -83,77 +79,60 @@ static void initInspectorServer() #endif } -WTF::String WebContext::platformDefaultApplicationCacheDirectory() const +WTF::String WebProcessPool::legacyPlatformDefaultApplicationCacheDirectory() { - GUniquePtr<gchar> cacheDirectory(g_build_filename(g_get_user_cache_dir(), "webkitgtk", "applications", nullptr)); - return WebCore::filenameToString(cacheDirectory.get()); + return API::WebsiteDataStore::defaultApplicationCacheDirectory(); } -void WebContext::platformInitializeWebProcess(WebProcessCreationParameters& parameters) +WTF::String WebProcessPool::legacyPlatformDefaultMediaCacheDirectory() { - initInspectorServer(); - - if (!parameters.urlSchemesRegisteredAsLocal.contains("resource")) { - WebCore::SchemeRegistry::registerURLSchemeAsLocal("resource"); - parameters.urlSchemesRegisteredAsLocal.append("resource"); - } - - if (!usesNetworkProcess()) { - parameters.urlSchemesRegisteredForCustomProtocols = supplement<WebSoupCustomProtocolRequestManager>()->registeredSchemesForCustomProtocols(); - - supplement<WebCookieManagerProxy>()->getCookiePersistentStorage(parameters.cookiePersistentStoragePath, parameters.cookiePersistentStorageType); - parameters.cookieAcceptPolicy = m_initialHTTPCookieAcceptPolicy; + return API::WebsiteDataStore::defaultMediaCacheDirectory(); +} - parameters.ignoreTLSErrors = m_ignoreTLSErrors; - } +void WebProcessPool::platformInitializeWebProcess(WebProcessCreationParameters& parameters) +{ + initInspectorServer(); + parameters.memoryCacheDisabled = m_memoryCacheDisabled || cacheModel() == CacheModelDocumentViewer; + parameters.proxySettings = m_networkProxySettings; +} - parameters.shouldTrackVisitedLinks = true; +void WebProcessPool::platformInvalidateContext() +{ } -void WebContext::platformInvalidateContext() +String WebProcessPool::legacyPlatformDefaultWebSQLDatabaseDirectory() { + return API::WebsiteDataStore::defaultWebSQLDatabaseDirectory(); } -String WebContext::platformDefaultDatabaseDirectory() const +String WebProcessPool::legacyPlatformDefaultIndexedDBDatabaseDirectory() { - GUniquePtr<gchar> databaseDirectory(g_build_filename(g_get_user_data_dir(), "webkitgtk", "databases", nullptr)); - return WebCore::filenameToString(databaseDirectory.get()); + return API::WebsiteDataStore::defaultIndexedDBDatabaseDirectory(); } -String WebContext::platformDefaultIconDatabasePath() const +String WebProcessPool::platformDefaultIconDatabasePath() const { GUniquePtr<gchar> databaseDirectory(g_build_filename(g_get_user_cache_dir(), "webkitgtk", "icondatabase", nullptr)); - return WebCore::filenameToString(databaseDirectory.get()); + return WebCore::stringFromFileSystemRepresentation(databaseDirectory.get()); } -String WebContext::platformDefaultLocalStorageDirectory() const +String WebProcessPool::legacyPlatformDefaultLocalStorageDirectory() { - GUniquePtr<gchar> storageDirectory(g_build_filename(g_get_user_data_dir(), "webkitgtk", "localstorage", nullptr)); - return WebCore::filenameToString(storageDirectory.get()); + return API::WebsiteDataStore::defaultLocalStorageDirectory(); } -String WebContext::platformDefaultDiskCacheDirectory() const +String WebProcessPool::legacyPlatformDefaultMediaKeysStorageDirectory() { - GUniquePtr<char> diskCacheDirectory(g_build_filename(g_get_user_cache_dir(), g_get_prgname(), nullptr)); - return WebCore::filenameToString(diskCacheDirectory.get()); + return API::WebsiteDataStore::defaultMediaKeysStorageDirectory(); } -String WebContext::platformDefaultCookieStorageDirectory() const +String WebProcessPool::legacyPlatformDefaultNetworkCacheDirectory() { - notImplemented(); - return String(); + return API::WebsiteDataStore::defaultNetworkCacheDirectory(); } -void WebContext::setIgnoreTLSErrors(bool ignoreTLSErrors) +void WebProcessPool::platformResolvePathsForSandboxExtensions() { - m_ignoreTLSErrors = ignoreTLSErrors; -#if ENABLE(NETWORK_PROCESS) - if (usesNetworkProcess() && networkProcess()) { - networkProcess()->send(Messages::NetworkProcess::SetIgnoreTLSErrors(m_ignoreTLSErrors), 0); - return; - } -#endif - sendToAllProcesses(Messages::WebProcess::SetIgnoreTLSErrors(m_ignoreTLSErrors)); } } // namespace WebKit |