summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/gtk
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk')
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.cpp85
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStore.h56
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.cpp165
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreWayland.h59
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.cpp206
-rw-r--r--Source/WebKit2/UIProcess/gtk/AcceleratedBackingStoreX11.h57
-rw-r--r--Source/WebKit2/UIProcess/gtk/DragAndDropHandler.cpp305
-rw-r--r--Source/WebKit2/UIProcess/gtk/DragAndDropHandler.h94
-rw-r--r--Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.cpp87
-rw-r--r--Source/WebKit2/UIProcess/gtk/ExperimentalFeatures.h55
-rw-r--r--Source/WebKit2/UIProcess/gtk/GestureController.cpp224
-rw-r--r--Source/WebKit2/UIProcess/gtk/GestureController.h111
-rw-r--r--Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.cpp87
-rw-r--r--Source/WebKit2/UIProcess/gtk/HardwareAccelerationManager.h48
-rw-r--r--Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp456
-rw-r--r--Source/WebKit2/UIProcess/gtk/InputMethodFilter.h127
-rw-r--r--Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp218
-rw-r--r--Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.h46
-rw-r--r--Source/WebKit2/UIProcess/gtk/TextCheckerGtk.cpp260
-rw-r--r--Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp521
-rw-r--r--Source/WebKit2/UIProcess/gtk/WaylandCompositor.h130
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.cpp115
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebColorPickerGtk.h (renamed from Source/WebKit2/UIProcess/gtk/WebProcessProxyGtk.cpp)60
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp204
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.h31
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.cpp9
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebInspectorClientGtk.h7
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebInspectorProxyGtk.cpp188
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp42
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebPasteboardProxyGtk.cpp70
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp237
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h49
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp30
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebProcessPoolGtk.cpp (renamed from Source/WebKit2/UIProcess/gtk/WebContextGtk.cpp)83
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