Implement VisualViewport API events
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Jan 2018 10:53:12 +0000 (10:53 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Jan 2018 10:53:12 +0000 (10:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179386

Patch by Ali Juma <ajuma@chromium.org> on 2018-01-09
Reviewed by Frédéric Wang.

LayoutTests/imported/w3c:

Update expectation for a viewport WPT that now passes.

* web-platform-tests/viewport/viewport-resize-event-on-load-overflowing-page-expected.txt:

Source/WebCore:

Implement the events (resize and scroll) defined by the Visual Viewport API
(https://wicg.github.io/visual-viewport/#events).

This is behind the VisualViewportAPI experimental feature flag.

In order to detect when events need to be fired, change the computation of
Visual Viewport attributes to happen whenever the layout viewport is updated
rather than only on-demand.

Tests: fast/visual-viewport/resize-event-fired-window-resized.html
       fast/visual-viewport/resize-event-fired.html
       fast/visual-viewport/scroll-event-fired.html

* dom/Document.cpp:
(WebCore::Document::addListenerTypeIfNeeded):
Add support for tracking resize event listeners.
* dom/Document.h:
* dom/DocumentEventQueue.cpp:
(WebCore::DocumentEventQueue::enqueueOrDispatchScrollEvent):
(WebCore::DocumentEventQueue::enqueueScrollEvent):
Factored out of enqueueOrDispatchScrollEvent so that this logic can be reused
for Visual Viewport scroll events.
(WebCore::DocumentEventQueue::enqueueResizeEvent):
(WebCore::DocumentEventQueue::pendingEventTimerFired):
* dom/DocumentEventQueue.h:
* page/FrameView.cpp:
(WebCore::FrameView::updateLayoutViewport):
* page/VisualViewport.cpp:
(WebCore::VisualViewport::addEventListener):
(WebCore::layoutIfNonNull):
(WebCore::VisualViewport::offsetLeft const):
Remove attribute computation logic since this now happens during update().
(WebCore::VisualViewport::offsetTop const): Ditto.
(WebCore::VisualViewport::pageLeft const): Ditto.
(WebCore::VisualViewport::pageTop const): Ditto.
(WebCore::VisualViewport::width const): Ditto.
(WebCore::VisualViewport::height const): Ditto.
(WebCore::VisualViewport::scale const):
(WebCore::VisualViewport::update):
Added. Computes all of the Visual Viewport attributes and determines
whether events need to be fired.
(WebCore::VisualViewport::enqueueResizeEvent):
(WebCore::VisualViewport::enqueueScrollEvent):
(WebCore::getFrameViewAndLayoutIfNonNull): Deleted.
* page/VisualViewport.h:

Source/WebKit:

Change the default value of the VisualViewportAPI experimental feature flag to
DEFAULT_EXPERIMENTAL_FEATURES_ENABLED. This patch completes the implementation
of this feature as specified by https://wicg.github.io/visual-viewport/, so this
feature is now ready for wider testing.

* Shared/WebPreferences.yaml:

LayoutTests:

Add tests for Visual Viewport API events. Test that a resize event is fired after
pinch zoom and after window resize, and test that a scroll event is fired when the
visual viewport is scrolled.

* fast/visual-viewport/resize-event-fired-expected.txt: Added.
* fast/visual-viewport/resize-event-fired-window-resized-expected.txt: Added.
* fast/visual-viewport/resize-event-fired-window-resized.html: Added.
* fast/visual-viewport/resize-event-fired.html: Added.
* fast/visual-viewport/scroll-event-fired-expected.txt: Added.
* fast/visual-viewport/scroll-event-fired.html: Added.
* platform/gtk/TestExpectations:
Skipped tests that use UIScriptController::zoomToScale, since this isn't implemented on GTK.
* platform/ios/TestExpectations:
Skipped test that resizes a window, since this isn't supported on iOS.
* platform/win/TestExpectations:
Skipped tests that use UIScriptController::zoomToScale, since this isn't implemented on Windows.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226622 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/visual-viewport/resize-event-fired-expected.txt [new file with mode: 0644]
LayoutTests/fast/visual-viewport/resize-event-fired-window-resized-expected.txt [new file with mode: 0644]
LayoutTests/fast/visual-viewport/resize-event-fired-window-resized.html [new file with mode: 0644]
LayoutTests/fast/visual-viewport/resize-event-fired.html [new file with mode: 0644]
LayoutTests/fast/visual-viewport/scroll-event-fired-expected.txt [new file with mode: 0644]
LayoutTests/fast/visual-viewport/scroll-event-fired.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/viewport/viewport-resize-event-on-load-overflowing-page-expected.txt
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/DocumentEventQueue.cpp
Source/WebCore/dom/DocumentEventQueue.h
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/VisualViewport.cpp
Source/WebCore/page/VisualViewport.h
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPreferences.yaml

index 4684551..9bd215a 100644 (file)
@@ -1,3 +1,27 @@
+2018-01-09  Ali Juma  <ajuma@chromium.org>
+
+        Implement VisualViewport API events
+        https://bugs.webkit.org/show_bug.cgi?id=179386
+
+        Reviewed by Frédéric Wang.
+
+        Add tests for Visual Viewport API events. Test that a resize event is fired after
+        pinch zoom and after window resize, and test that a scroll event is fired when the
+        visual viewport is scrolled.
+
+        * fast/visual-viewport/resize-event-fired-expected.txt: Added.
+        * fast/visual-viewport/resize-event-fired-window-resized-expected.txt: Added.
+        * fast/visual-viewport/resize-event-fired-window-resized.html: Added.
+        * fast/visual-viewport/resize-event-fired.html: Added.
+        * fast/visual-viewport/scroll-event-fired-expected.txt: Added.
+        * fast/visual-viewport/scroll-event-fired.html: Added.
+        * platform/gtk/TestExpectations:
+        Skipped tests that use UIScriptController::zoomToScale, since this isn't implemented on GTK.
+        * platform/ios/TestExpectations:
+        Skipped test that resizes a window, since this isn't supported on iOS.
+        * platform/win/TestExpectations:
+        Skipped tests that use UIScriptController::zoomToScale, since this isn't implemented on Windows.
+
 2018-01-09  Ryosuke Niwa  <rniwa@webkit.org>
 
         Release assert in addResourceTiming when a cache resource is requested during style recalc
diff --git a/LayoutTests/fast/visual-viewport/resize-event-fired-expected.txt b/LayoutTests/fast/visual-viewport/resize-event-fired-expected.txt
new file mode 100644 (file)
index 0000000..9178cad
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Verify a resize event gets fired on window.visualViewport after pinch-zoom 
+
diff --git a/LayoutTests/fast/visual-viewport/resize-event-fired-window-resized-expected.txt b/LayoutTests/fast/visual-viewport/resize-event-fired-window-resized-expected.txt
new file mode 100644 (file)
index 0000000..3516c6e
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Verify that a resize event gets fired when the window is resized. 
+
diff --git a/LayoutTests/fast/visual-viewport/resize-event-fired-window-resized.html b/LayoutTests/fast/visual-viewport/resize-event-fired-window-resized.html
new file mode 100644 (file)
index 0000000..92c2b5a
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    if (window.internals)
+        internals.settings.setVisualViewportEnabled(true);
+
+    var test = async_test('Verify that a resize event gets fired when the window is resized.');
+
+    function resizeHandler() {
+        test.done();
+    }
+
+    window.onload = function() {
+        window.visualViewport.addEventListener('resize', resizeHandler);
+        window.resizeTo(window.outerWidth - 40, window.outerHeight + 40);
+    };
+</script>
diff --git a/LayoutTests/fast/visual-viewport/resize-event-fired.html b/LayoutTests/fast/visual-viewport/resize-event-fired.html
new file mode 100644 (file)
index 0000000..e17263c
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<style>
+  body {
+    height: 2000px;
+    width: 2000px;
+  }
+</style>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    var pageScaleFactor = 2;
+
+    if (window.internals)
+        internals.settings.setVisualViewportEnabled(true);
+
+    var test = async_test('Verify a resize event gets fired on window.visualViewport after pinch-zoom');
+
+    function resizeHandler() {
+        test.done();
+    }
+
+    function getUIScript() {
+        return `(function() {
+            uiController.zoomToScale(${pageScaleFactor}, function() {
+                uiController.uiScriptComplete(uiController.zoomScale);
+            });
+        })();`;
+    }
+
+    window.onload = function() {
+        window.visualViewport.addEventListener('resize', resizeHandler);
+        testRunner.runUIScript(getUIScript(), function() {});
+    };
+</script>
diff --git a/LayoutTests/fast/visual-viewport/scroll-event-fired-expected.txt b/LayoutTests/fast/visual-viewport/scroll-event-fired-expected.txt
new file mode 100644 (file)
index 0000000..331b52b
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Verify that a scroll event gets fired on window.visualViewport when its offset changes 
+
diff --git a/LayoutTests/fast/visual-viewport/scroll-event-fired.html b/LayoutTests/fast/visual-viewport/scroll-event-fired.html
new file mode 100644 (file)
index 0000000..e00db02
--- /dev/null
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+  body {
+    height: 2000px;
+    width: 2000px;
+  }
+</style>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    var pageScaleFactor = 2;
+
+    if (window.internals)
+        internals.settings.setVisualViewportEnabled(true);
+
+    var test = async_test('Verify that a scroll event gets fired on window.visualViewport when its offset changes');
+
+    function scrollEventHandler() {
+        test.done();
+    }
+
+    function doAfterZooming() {
+        window.visualViewport.addEventListener('scroll', scrollEventHandler);
+        window.scrollTo(1000, 1000);
+    }
+
+    function getUIScript() {
+        return `(function() {
+            uiController.zoomToScale(${pageScaleFactor}, function() {
+                uiController.uiScriptComplete(uiController.zoomScale);
+            });
+        })();`;
+    }
+
+    window.onload = function() {
+        testRunner.runUIScript(getUIScript(), doAfterZooming);
+    };
+</script>
index 982b4ea..885f635 100644 (file)
@@ -1,3 +1,14 @@
+2018-01-09  Ali Juma  <ajuma@chromium.org>
+
+        Implement VisualViewport API events
+        https://bugs.webkit.org/show_bug.cgi?id=179386
+
+        Reviewed by Frédéric Wang.
+
+        Update expectation for a viewport WPT that now passes.
+
+        * web-platform-tests/viewport/viewport-resize-event-on-load-overflowing-page-expected.txt:
+
 2018-01-08  Chris Nardi  <csnardi1@gmail.com>
 
         ::first-letter incorrectly selects grapheme pairs
index 00ef16d..4fd6370 100644 (file)
@@ -3,5 +3,5 @@ Viewport: Resize Event On Load Overflowing Page
 Test Description: This test ensures that we fire a resize event against window.visualViewport if the page has overflow (since this creates a scrollbar and thus changes the viewport size).
 
 
-FAIL Resize event fired exactly once against window.visualViewport if scrollbars affect layout. assert_equals: expected 1 but got 0
+PASS Resize event fired exactly once against window.visualViewport if scrollbars affect layout. 
 
index a60aabc..f6f9f26 100644 (file)
@@ -968,7 +968,9 @@ webkit.org/b/153833 fast/events/before-input-prevent-insert-replacement.html [ S
 webkit.org/b/153833 fast/events/input-event-insert-replacement.html [ Skip ]
 
 # Skip tests requiring UIScriptController::zoomToScale
+webkit.org/b/168050 fast/visual-viewport/resize-event-fired.html [ Skip ]
 webkit.org/b/168050 fast/visual-viewport/rtl-zoomed-rects.html [ Skip ]
+webkit.org/b/168050 fast/visual-viewport/scroll-event-fired.html [ Skip ]
 webkit.org/b/168050 fast/visual-viewport/viewport-dimensions-exclude-custom-scrollbars.html [ Skip ]
 webkit.org/b/168050 fast/visual-viewport/viewport-dimensions-exclude-scrollbars.html [ Skip ]
 webkit.org/b/168050 fast/visual-viewport/viewport-dimensions-iframe.html [ Skip ]
index b7c485f..964a5fe 100644 (file)
@@ -2036,6 +2036,7 @@ fast/css-grid-layout/flex-content-sized-columns-resize.html [ Skip ]
 fast/dom/Window/window-resize-update-scrollbars.html [ Skip ]
 fast/dynamic/window-resize-scrollbars-test.html [ Skip ]
 fast/images/animated-gif-window-resizing.html [ Skip ]
+fast/visual-viewport/resize-event-fired-window-resized.html [ Skip ]
 loader/go-back-to-different-window-size.html [ Skip ]
 webkit.org/b/178739 css3/viewport-percentage-lengths/viewport-percentage-lengths-anonymous-block.html [ Skip ]
 webkit.org/b/178739 css3/viewport-percentage-lengths/viewport-percentage-lengths-percent-size-child.html [ Skip ]
index e66e93c..48d04ce 100644 (file)
@@ -629,7 +629,9 @@ webkit.org/b/158836 imported/w3c/web-platform-tests/encrypted-media [ Skip ]
 http/wpt/credential-management/ [ Skip ]
 
 # UIScriptController::zoomToScale is not implemented on Windows.
+webkit.org/b/180424 fast/visual-viewport/resize-event-fired.html [ Skip ]
 webkit.org/b/180424 fast/visual-viewport/rtl-zoomed-rects.html [ Skip ]
+webkit.org/b/180424 fast/visual-viewport/scroll-event-fired.html [ Skip ]
 webkit.org/b/180424 fast/visual-viewport/viewport-dimensions-exclude-custom-scrollbars.html [ Skip ]
 webkit.org/b/180424 fast/visual-viewport/viewport-dimensions-exclude-scrollbars.html [ Skip ]
 webkit.org/b/180424 fast/visual-viewport/viewport-dimensions-iframe.html [ Skip ]
index fd56540..31c1bb0 100644 (file)
@@ -1,3 +1,56 @@
+2018-01-09  Ali Juma  <ajuma@chromium.org>
+
+        Implement VisualViewport API events
+        https://bugs.webkit.org/show_bug.cgi?id=179386
+
+        Reviewed by Frédéric Wang.
+
+        Implement the events (resize and scroll) defined by the Visual Viewport API
+        (https://wicg.github.io/visual-viewport/#events).
+
+        This is behind the VisualViewportAPI experimental feature flag.
+
+        In order to detect when events need to be fired, change the computation of
+        Visual Viewport attributes to happen whenever the layout viewport is updated
+        rather than only on-demand.
+
+        Tests: fast/visual-viewport/resize-event-fired-window-resized.html
+               fast/visual-viewport/resize-event-fired.html
+               fast/visual-viewport/scroll-event-fired.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::addListenerTypeIfNeeded):
+        Add support for tracking resize event listeners.
+        * dom/Document.h:
+        * dom/DocumentEventQueue.cpp:
+        (WebCore::DocumentEventQueue::enqueueOrDispatchScrollEvent):
+        (WebCore::DocumentEventQueue::enqueueScrollEvent):
+        Factored out of enqueueOrDispatchScrollEvent so that this logic can be reused
+        for Visual Viewport scroll events.
+        (WebCore::DocumentEventQueue::enqueueResizeEvent):
+        (WebCore::DocumentEventQueue::pendingEventTimerFired):
+        * dom/DocumentEventQueue.h:
+        * page/FrameView.cpp:
+        (WebCore::FrameView::updateLayoutViewport):
+        * page/VisualViewport.cpp:
+        (WebCore::VisualViewport::addEventListener):
+        (WebCore::layoutIfNonNull):
+        (WebCore::VisualViewport::offsetLeft const):
+        Remove attribute computation logic since this now happens during update().
+        (WebCore::VisualViewport::offsetTop const): Ditto.
+        (WebCore::VisualViewport::pageLeft const): Ditto.
+        (WebCore::VisualViewport::pageTop const): Ditto.
+        (WebCore::VisualViewport::width const): Ditto.
+        (WebCore::VisualViewport::height const): Ditto.
+        (WebCore::VisualViewport::scale const):
+        (WebCore::VisualViewport::update):
+        Added. Computes all of the Visual Viewport attributes and determines
+        whether events need to be fired.
+        (WebCore::VisualViewport::enqueueResizeEvent):
+        (WebCore::VisualViewport::enqueueScrollEvent):
+        (WebCore::getFrameViewAndLayoutIfNonNull): Deleted.
+        * page/VisualViewport.h:
+
 2018-01-09  Yacine Bandou  <yacine.bandou_ext@softathome.com>
 
         [EME] Add the CENC initData support in ClearKey CDM
index 62ce513..04400dd 100644 (file)
@@ -4464,6 +4464,8 @@ void Document::addListenerTypeIfNeeded(const AtomicString& eventType)
         addListenerType(FORCEDOWN_LISTENER);
     else if (eventType == eventNames().webkitmouseforceupEvent)
         addListenerType(FORCEUP_LISTENER);
+    else if (eventType == eventNames().resizeEvent)
+        addListenerType(RESIZE_LISTENER);
 }
 
 CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&)
index 4d2ed6b..9e6e2b3 100644 (file)
@@ -825,7 +825,8 @@ public:
         FORCEWILLBEGIN_LISTENER              = 1 << 13,
         FORCECHANGED_LISTENER                = 1 << 14,
         FORCEDOWN_LISTENER                   = 1 << 15,
-        FORCEUP_LISTENER                     = 1 << 16
+        FORCEUP_LISTENER                     = 1 << 16,
+        RESIZE_LISTENER                      = 1 << 17
     };
 
     bool hasListenerType(ListenerType listenerType) const { return (m_listenerTypes & listenerType); }
index f66f437..6f05d24 100644 (file)
@@ -85,24 +85,44 @@ void DocumentEventQueue::enqueueOrDispatchScrollEvent(Node& target)
 {
     ASSERT(&target.document() == &m_document);
 
+    // Per the W3C CSSOM View Module, scroll events fired at the document should bubble, others should not.
+    bool bubbles = target.isDocumentNode();
+    bool cancelable = false;
+    enqueueScrollEvent(target, bubbles, cancelable);
+}
+
+void DocumentEventQueue::enqueueScrollEvent(EventTarget& target, bool bubbles, bool cancelable)
+{
     if (m_isClosed)
         return;
 
     if (!m_document.hasListenerType(Document::SCROLL_LISTENER))
         return;
 
-    if (!m_nodesWithQueuedScrollEvents.add(&target).isNewEntry)
+    if (!m_targetsWithQueuedScrollEvents.add(&target).isNewEntry)
         return;
 
-    // Per the W3C CSSOM View Module, scroll events fired at the document should bubble, others should not.
-    bool bubbles = target.isDocumentNode();
-    bool cancelable = false;
-
     Ref<Event> scrollEvent = Event::create(eventNames().scrollEvent, bubbles, cancelable);
     scrollEvent->setTarget(&target);
     enqueueEvent(WTFMove(scrollEvent));
 }
 
+void DocumentEventQueue::enqueueResizeEvent(EventTarget& target, bool bubbles, bool cancelable)
+{
+    if (m_isClosed)
+        return;
+
+    if (!m_document.hasListenerType(Document::RESIZE_LISTENER))
+        return;
+
+    if (!m_targetsWithQueuedResizeEvents.add(&target).isNewEntry)
+        return;
+
+    Ref<Event> resizeEvent = Event::create(eventNames().resizeEvent, bubbles, cancelable);
+    resizeEvent->setTarget(&target);
+    enqueueEvent(WTFMove(resizeEvent));
+}
+
 bool DocumentEventQueue::cancelEvent(Event& event)
 {
     bool found = m_queuedEvents.remove(&event);
@@ -123,7 +143,8 @@ void DocumentEventQueue::pendingEventTimerFired()
     ASSERT(!m_pendingEventTimer->isActive());
     ASSERT(!m_queuedEvents.isEmpty());
 
-    m_nodesWithQueuedScrollEvents.clear();
+    m_targetsWithQueuedScrollEvents.clear();
+    m_targetsWithQueuedResizeEvents.clear();
 
     // Insert a marker for where we should stop.
     ASSERT(!m_queuedEvents.contains(nullptr));
index 973b0d1..b2f1ba0 100644 (file)
@@ -48,6 +48,8 @@ public:
     void close() override;
 
     void enqueueOrDispatchScrollEvent(Node&);
+    void enqueueScrollEvent(EventTarget&, bool bubbles, bool cancelable);
+    void enqueueResizeEvent(EventTarget&, bool bubbles, bool cancelable);
 
 private:
     void pendingEventTimerFired();
@@ -58,7 +60,8 @@ private:
     Document& m_document;
     std::unique_ptr<Timer> m_pendingEventTimer;
     ListHashSet<RefPtr<Event>> m_queuedEvents;
-    HashSet<Node*> m_nodesWithQueuedScrollEvents;
+    HashSet<EventTarget*> m_targetsWithQueuedScrollEvents;
+    HashSet<EventTarget*> m_targetsWithQueuedResizeEvents;
     bool m_isClosed;
 };
 
index e4c1dca..cb8dd1a 100644 (file)
@@ -94,6 +94,7 @@
 #include "StyleScope.h"
 #include "TextResourceDecoder.h"
 #include "TiledBacking.h"
+#include "VisualViewport.h"
 #include "WheelEventTestTrigger.h"
 #include <wtf/text/TextStream.h>
 
@@ -1673,6 +1674,10 @@ void FrameView::updateLayoutViewport()
             LayoutPoint newOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, StickToDocumentBounds);
             setLayoutViewportOverrideRect(LayoutRect(newOrigin, m_layoutViewportOverrideRect.value().size()));
         }
+        if (frame().settings().visualViewportAPIEnabled()) {
+            if (Document* document = frame().document())
+                document->domWindow()->visualViewport()->update();
+        }
         return;
     }
 
@@ -1681,6 +1686,10 @@ void FrameView::updateLayoutViewport()
         setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
         LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect());
     }
+    if (frame().settings().visualViewportAPIEnabled()) {
+        if (Document* document = frame().document())
+            document->domWindow()->visualViewport()->update();
+    }
 }
 
 LayoutPoint FrameView::minStableLayoutViewportOrigin() const
index 63e897b..21c1c3f 100644 (file)
@@ -29,6 +29,9 @@
 #include "ContextDestructionObserver.h"
 #include "DOMWindow.h"
 #include "Document.h"
+#include "DocumentEventQueue.h"
+#include "Event.h"
+#include "EventNames.h"
 #include "Frame.h"
 #include "FrameView.h"
 #include "Page.h"
@@ -52,56 +55,74 @@ ScriptExecutionContext* VisualViewport::scriptExecutionContext() const
     return static_cast<ContextDestructionObserver*>(m_associatedDOMWindow)->scriptExecutionContext();
 }
 
-static FrameView* getFrameViewAndLayoutIfNonNull(Frame* frame)
+bool VisualViewport::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    auto* view = frame ? frame->view() : nullptr;
-    if (view) {
-        ASSERT(frame->pageZoomFactor());
-        frame->document()->updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks::Synchronously);
-    }
-    return view;
+    if (!EventTarget::addEventListener(eventType, WTFMove(listener), options))
+        return false;
+
+    if (m_frame)
+        m_frame->document()->addListenerTypeIfNeeded(eventType);
+    return true;
+}
+
+void VisualViewport::updateFrameLayout() const
+{
+    ASSERT(m_frame);
+    m_frame->document()->updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks::Synchronously);
 }
 
 double VisualViewport::offsetLeft() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return (view->visualViewportRect().x() - view->layoutViewportRect().x()) / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_offsetLeft;
 }
 
 double VisualViewport::offsetTop() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return (view->visualViewportRect().y() - view->layoutViewportRect().y()) / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_offsetTop;
 }
 
 double VisualViewport::pageLeft() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return view->visualViewportRect().x() / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_pageLeft;
 }
 
 double VisualViewport::pageTop() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return view->visualViewportRect().y() / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_pageTop;
 }
 
 double VisualViewport::width() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return view->visualViewportRect().width() / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_width;
 }
 
 double VisualViewport::height() const
 {
-    if (auto* view = getFrameViewAndLayoutIfNonNull(m_frame))
-        return view->visualViewportRect().height() / m_frame->pageZoomFactor();
-    return 0;
+    if (!m_frame)
+        return 0;
+
+    updateFrameLayout();
+    return m_height;
 }
 
 double VisualViewport::scale() const
@@ -110,11 +131,68 @@ double VisualViewport::scale() const
     if (!m_frame || !m_frame->isMainFrame())
         return 1;
 
-    if (auto* page = m_frame->page()) {
-        m_frame->document()->updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks::Synchronously);
-        return page->pageScaleFactor();
+    updateFrameLayout();
+    return m_scale;
+}
+
+void VisualViewport::update()
+{
+    double offsetLeft = 0;
+    double offsetTop = 0;
+    m_pageLeft = 0;
+    m_pageTop = 0;
+    double width = 0;
+    double height = 0;
+    double scale = 1;
+
+    if (m_frame) {
+        if (auto* view = m_frame->view()) {
+            auto visualViewportRect = view->visualViewportRect();
+            auto layoutViewportRect = view->layoutViewportRect();
+            auto pageZoomFactor = m_frame->pageZoomFactor();
+            ASSERT(pageZoomFactor);
+            offsetLeft = (visualViewportRect.x() - layoutViewportRect.x()) / pageZoomFactor;
+            offsetTop = (visualViewportRect.y() - layoutViewportRect.y()) / pageZoomFactor;
+            m_pageLeft = visualViewportRect.x() / pageZoomFactor;
+            m_pageTop = visualViewportRect.y() / pageZoomFactor;
+            width = visualViewportRect.width() / pageZoomFactor;
+            height = visualViewportRect.height() / pageZoomFactor;
+        }
+        if (auto* page = m_frame->page())
+            scale = page->pageScaleFactor();
+    }
+
+    if (m_offsetLeft != offsetLeft || m_offsetTop != offsetTop) {
+        enqueueScrollEvent();
+        m_offsetLeft = offsetLeft;
+        m_offsetTop = offsetTop;
     }
-    return 1;
+    if (m_width != width || m_height != height || m_scale != scale) {
+        enqueueResizeEvent();
+        m_width = width;
+        m_height = height;
+        m_scale = scale;
+    }
+}
+
+void VisualViewport::enqueueResizeEvent()
+{
+    if (!m_frame)
+        return;
+
+    bool bubbles = false;
+    bool cancelable = false;
+    m_frame->document()->eventQueue().enqueueResizeEvent(*this, bubbles, cancelable);
+}
+
+void VisualViewport::enqueueScrollEvent()
+{
+    if (!m_frame)
+        return;
+
+    bool bubbles = false;
+    bool cancelable = false;
+    m_frame->document()->eventQueue().enqueueScrollEvent(*this, bubbles, cancelable);
 }
 
 } // namespace WebCore
index 15f7fe7..47727d3 100644 (file)
@@ -37,8 +37,10 @@ class VisualViewport final : public RefCounted<VisualViewport>, public EventTarg
 public:
     static Ref<VisualViewport> create(Frame* frame) { return adoptRef(*new VisualViewport(frame)); }
 
+    // EventTarget
     EventTargetInterface eventTargetInterface() const final;
     ScriptExecutionContext* scriptExecutionContext() const final;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final;
 
     double offsetLeft() const;
     double offsetTop() const;
@@ -48,6 +50,8 @@ public:
     double height() const;
     double scale() const;
 
+    void update();
+
     using RefCounted::ref;
     using RefCounted::deref;
 
@@ -56,6 +60,19 @@ private:
 
     void refEventTarget() final { ref(); }
     void derefEventTarget() final { deref(); }
+
+    void enqueueResizeEvent();
+    void enqueueScrollEvent();
+
+    void updateFrameLayout() const;
+
+    double m_offsetLeft { 0 };
+    double m_offsetTop { 0 };
+    double m_pageLeft { 0 };
+    double m_pageTop { 0 };
+    double m_width { 0 };
+    double m_height { 0 };
+    double m_scale { 1 };
 };
 
 } // namespace WebCore
index b98ddd0..2eb718f 100644 (file)
@@ -1,3 +1,17 @@
+2018-01-09  Ali Juma  <ajuma@chromium.org>
+
+        Implement VisualViewport API events
+        https://bugs.webkit.org/show_bug.cgi?id=179386
+
+        Reviewed by Frédéric Wang.
+
+        Change the default value of the VisualViewportAPI experimental feature flag to
+        DEFAULT_EXPERIMENTAL_FEATURES_ENABLED. This patch completes the implementation
+        of this feature as specified by https://wicg.github.io/visual-viewport/, so this
+        feature is now ready for wider testing.
+
+        * Shared/WebPreferences.yaml:
+
 2018-01-08  Alex Christensen  <achristensen@webkit.org>
 
         Add WKNavigationDelegate SPI exposing WebProcess crash reason
index 2bc4d20..af74da3 100644 (file)
@@ -1146,7 +1146,7 @@ SubresourceIntegrityEnabled:
 
 VisualViewportAPIEnabled:
   type: bool
-  defaultValue: false
+  defaultValue: DEFAULT_EXPERIMENTAL_FEATURES_ENABLED
   humanReadableName: "VisualViewportAPI"
   humanReadableDescription: "Enable Visual Viewport API"
   category: experimental