Skip to content
Permalink
Browse files
Add a setting to suppress keyboard input during provisional navigation
https://bugs.webkit.org/show_bug.cgi?id=165830

Reviewed by Brent Fulgham.

Source/WebCore:

Added a setting that suppresses keyboard input during provisional navigation. When the
setting is enabled, DOM propogation of keyboard input events (KeyboardEvents,
CompositionEvents, InputEvents, and some TextEvents) is suppressed, and text insertion is
disabled in the editor.

Non-editing default event handling still occurs, for instance keyboard scrolling, access
keys, and focus navigation.

Test: http/tests/navigation/keyboard-events-during-provisional-navigation.html

* dom/CompositionEvent.h: Added a type trait specialization.
* dom/EventDispatcher.cpp:
(WebCore::shouldSuppressEventDispatchInDOM): Added. Returns true if the event is trusted,
FrameLoader::shouldSuppressKeyboardInput() returns true, and the event is a
CompositionEvent, InputEvent, KeyboardEvent, or keyboard/composition TextEvent.
(WebCore::EventDispatcher::dispatchEvent): Called stopPropogation() on the event if
shouldSuppressEventDispatchInDOM() returns true.
* dom/InputEvent.h: Removed the inline no-op destructor so that DataTransfer does not need
to be a complete type in every translation unit that includes this header. Added a type
trait specialization.
* dom/TextEvent.h: Added isKeyboard() to determine if m_inputType is TextEventInputKeyboard.
* editing/Editor.cpp:
(WebCore::Editor::shouldInsertText): Returned false if
FrameLoader::shouldSuppressKeyboardInput() returns true and the action is
EditorInsertActionTyped.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::shouldSuppressKeyboardInput): Added. Returns true if
Settings::shouldSuppressKeyboardInputDuringProvisionalNavigation() returns true and the
state is FrameStateProvisional.
* loader/FrameLoader.h: Declared shouldSuppressKeyboardInput().
* page/EventHandler.h: Exported accessKeyModifiers().
* page/Settings.in: Defined shouldSuppressKeyboardInputDuringProvisionalNavigation with an
initial value of false.
* testing/Internals.cpp:
(WebCore::Internals::accessKeyModifiers): Added. Returns a vector of strings representing
the access key modifiers.
* testing/Internals.h: Declared accessKeyModifiers().
* testing/Internals.idl: Exposed accessKeyModifiers() on the internals object.

Source/WebKit2:

* Shared/WebPreferencesDefinitions.h: Defined
shouldSuppressKeyboardDOMEventsDuringProvisionalNavigation.
* UIProcess/API/C/WKPreferences.cpp:
(WKPreferencesSetShouldSuppressKeyboardInputDuringProvisionalNavigation): Added.
(WKPreferencesGetShouldSuppressKeyboardInputDuringProvisionalNavigation): Added.
* UIProcess/API/C/WKPreferencesRefPrivate.h: Declared a getter and setter for the new
preference.
* UIProcess/API/Cocoa/WKPreferences.mm:
(-[WKPreferences _shouldSuppressKeyboardInputDuringProvisionalNavigation]): Added.
(-[WKPreferences _setShouldSuppressKeyboardInputDuringProvisionalNavigation:]): Added.
* UIProcess/API/Cocoa/WKPreferencesPrivate.h: Declared a property for the new preference.
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updatePreferences): Mapped the new preference to its corresponding
WebCore::Setting.

LayoutTests:

* http/tests/navigation/keyboard-events-during-provisional-navigation-expected.txt: Added.
* http/tests/navigation/keyboard-events-during-provisional-navigation.html: Added.
* http/tests/navigation/resources/keyboard-events-after-navigation.html: Added.
* http/tests/navigation/resources/keyboard-events-test.js: Added.
(runTest.eventHandler):
(runTest):
* http/tests/navigation/resources/never-respond.php: Added.
* platform/ios-simulator/TestExpectations:


Canonical link: https://commits.webkit.org/183574@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209943 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
aestes committed Dec 16, 2016
1 parent 3004eea commit bc7fb4750d5e979ecdbd716c566ed18a4c550854
Showing with 327 additions and 11 deletions.
  1. +16 −0 LayoutTests/ChangeLog
  2. +40 −0 LayoutTests/http/tests/navigation/keyboard-events-during-provisional-navigation-expected.txt
  3. +39 −0 LayoutTests/http/tests/navigation/keyboard-events-during-provisional-navigation.html
  4. +12 −0 LayoutTests/http/tests/navigation/resources/keyboard-events-after-navigation.html
  5. +40 −0 LayoutTests/http/tests/navigation/resources/keyboard-events-test.js
  6. +4 −0 LayoutTests/http/tests/navigation/resources/never-respond.php
  7. +1 −0 LayoutTests/platform/ios-simulator/TestExpectations
  8. +46 −0 Source/WebCore/ChangeLog
  9. +2 −0 Source/WebCore/dom/CompositionEvent.h
  10. +29 −1 Source/WebCore/dom/EventDispatcher.cpp
  11. +2 −2 Source/WebCore/dom/InputEvent.h
  12. +1 −0 Source/WebCore/dom/TextEvent.h
  13. +4 −1 Source/WebCore/editing/Editor.cpp
  14. +5 −0 Source/WebCore/loader/FrameLoader.cpp
  15. +1 −0 Source/WebCore/loader/FrameLoader.h
  16. +1 −1 Source/WebCore/page/EventHandler.h
  17. +2 −0 Source/WebCore/page/Settings.in
  18. +26 −0 Source/WebCore/testing/Internals.cpp
  19. +2 −0 Source/WebCore/testing/Internals.h
  20. +2 −0 Source/WebCore/testing/Internals.idl
  21. +22 −0 Source/WebKit2/ChangeLog
  22. +2 −4 Source/WebKit2/Shared/WebPreferencesDefinitions.h
  23. +10 −0 Source/WebKit2/UIProcess/API/C/WKPreferences.cpp
  24. +4 −0 Source/WebKit2/UIProcess/API/C/WKPreferencesRefPrivate.h
  25. +11 −1 Source/WebKit2/UIProcess/API/Cocoa/WKPreferences.mm
  26. +2 −1 Source/WebKit2/UIProcess/API/Cocoa/WKPreferencesPrivate.h
  27. +1 −0 Source/WebKit2/WebProcess/WebPage/WebPage.cpp
@@ -1,3 +1,19 @@
2016-12-16 Andy Estes <aestes@apple.com>

Add a setting to suppress keyboard input during provisional navigation
https://bugs.webkit.org/show_bug.cgi?id=165830

Reviewed by Brent Fulgham.

* http/tests/navigation/keyboard-events-during-provisional-navigation-expected.txt: Added.
* http/tests/navigation/keyboard-events-during-provisional-navigation.html: Added.
* http/tests/navigation/resources/keyboard-events-after-navigation.html: Added.
* http/tests/navigation/resources/keyboard-events-test.js: Added.
(runTest.eventHandler):
(runTest):
* http/tests/navigation/resources/never-respond.php: Added.
* platform/ios-simulator/TestExpectations:

2016-12-16 Chris Dumez <cdumez@apple.com>

HTML form validation bubble should be dismissed on navigation
@@ -0,0 +1,40 @@
CONSOLE MESSAGE: line 30: Provisional navigation started.
CONSOLE MESSAGE: line 31: No trusted events should be logged and the input element should have the value "".
CONSOLE MESSAGE: line 18: Dispatching untrusted keypress event.
CONSOLE MESSAGE: line 5: keypressevent dispatched (isTrusted: false).
CONSOLE MESSAGE: line 26: Pressing tab.
CONSOLE MESSAGE: line 28: Active element after pressing tab: [object HTMLInputElement].
CONSOLE MESSAGE: line 30: Pressing "a".
CONSOLE MESSAGE: line 33: Setting marked text to "b".
CONSOLE MESSAGE: line 36: Inserting text "c".
CONSOLE MESSAGE: line 39: Input element value after text input events: "".
CONSOLE MESSAGE: line 34: Pressing "z" with access key modifiers should navigate to resources/keyboard-events-after-navigation.html.
CONSOLE MESSAGE: line 6: Finished navigating to resources/keyboard-events-after-navigation.html.
CONSOLE MESSAGE: line 7: Trusted events should be logged and the input element should have the value "ac".
CONSOLE MESSAGE: line 18: Dispatching untrusted keypress event.
CONSOLE MESSAGE: line 5: keypressevent dispatched (isTrusted: false).
CONSOLE MESSAGE: line 26: Pressing tab.
CONSOLE MESSAGE: line 5: keydownevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: keyupevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 28: Active element after pressing tab: [object HTMLInputElement].
CONSOLE MESSAGE: line 30: Pressing "a".
CONSOLE MESSAGE: line 5: keydownevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: keypressevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: textInputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: beforeinputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: inputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: keyupevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 33: Setting marked text to "b".
CONSOLE MESSAGE: line 5: compositionstartevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: compositionupdateevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: beforeinputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: inputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 36: Inserting text "c".
CONSOLE MESSAGE: line 5: beforeinputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: inputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: textInputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: beforeinputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: inputevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 5: compositionendevent dispatched (isTrusted: true).
CONSOLE MESSAGE: line 39: Input element value after text input events: "ac".

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<body>
<a href="resources/keyboard-events-after-navigation.html" accesskey="z"></a>
<script src="resources/keyboard-events-test.js"></script>
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
internals.settings.setShouldSuppressKeyboardInputDuringProvisionalNavigation(true);
}

function waitForProvisionalNavigation(completionHandler)
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState !== this.DONE)
return;
window.setTimeout(function() {
completionHandler();
}, 0);
};
xhr.open("GET", "resources/never-respond.php");
xhr.send();

window.location = "resources/never-respond.php";
}

waitForProvisionalNavigation(function() {
console.log("Provisional navigation started.");
console.log("No trusted events should be logged and the input element should have the value \"\".");
runTest();

console.log("Pressing \"z\" with access key modifiers should navigate to resources/keyboard-events-after-navigation.html.");
eventSender.keyDown("z", internals.accessKeyModifiers());
});
</script>
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<script src="keyboard-events-test.js"></script>
<script>
console.log("Finished navigating to resources/keyboard-events-after-navigation.html.");
console.log("Trusted events should be logged and the input element should have the value \"ac\".");
runTest();
testRunner.notifyDone();
</script>
</body>
</html>
@@ -0,0 +1,40 @@
function runTest()
{
function eventHandler(event)
{
console.log(event.type + "event dispatched (isTrusted: " + event.isTrusted + ").");
}

window.addEventListener("keydown", eventHandler, true);
window.addEventListener("keypress", eventHandler, true);
window.addEventListener("keyup", eventHandler, true);
window.addEventListener("compositionstart", eventHandler, true);
window.addEventListener("compositionupdate", eventHandler, true);
window.addEventListener("compositionend", eventHandler, true);
window.addEventListener("textInput", eventHandler, true);
window.addEventListener("beforeinput", eventHandler, true);
window.addEventListener("input", eventHandler, true);

console.log("Dispatching untrusted keypress event.");
var keyPressEvent = new KeyboardEvent("keypress");
document.body.dispatchEvent(keyPressEvent);

var textInput = document.createElement("input");
textInput.type = "text";
document.body.appendChild(textInput);

console.log("Pressing tab.");
eventSender.keyDown("\t");
console.log("Active element after pressing tab: " + document.activeElement + ".");

console.log("Pressing \"a\".");
eventSender.keyDown("a");

console.log("Setting marked text to \"b\".");
textInputController.setMarkedText("b", 0, 1);

console.log("Inserting text \"c\".");
textInputController.insertText("c");

console.log("Input element value after text input events: \"" + textInput.value + "\".");
}
@@ -0,0 +1,4 @@
<?php
for (;;)
sleep(1);
?>
@@ -355,6 +355,7 @@ fast/shadow-dom/activate-over-slotted-content.html [ Skip ]
fast/shadow-dom/hover-over-slotted-content.html [ Skip ]
fast/events/keyboardevent-key.html [ Skip ]
fast/events/keyboardevent-code.html [ Skip ]
http/tests/navigation/keyboard-events-during-provisional-navigation.html [ Skip ]

# The file-wrapper part of <attachment> is not yet working on iOS
fast/attachment/attachment-type-attribute.html [ Skip ]
@@ -1,3 +1,49 @@
2016-12-16 Andy Estes <aestes@apple.com>

Add a setting to suppress keyboard input during provisional navigation
https://bugs.webkit.org/show_bug.cgi?id=165830

Reviewed by Brent Fulgham.

Added a setting that suppresses keyboard input during provisional navigation. When the
setting is enabled, DOM propogation of keyboard input events (KeyboardEvents,
CompositionEvents, InputEvents, and some TextEvents) is suppressed, and text insertion is
disabled in the editor.

Non-editing default event handling still occurs, for instance keyboard scrolling, access
keys, and focus navigation.

Test: http/tests/navigation/keyboard-events-during-provisional-navigation.html

* dom/CompositionEvent.h: Added a type trait specialization.
* dom/EventDispatcher.cpp:
(WebCore::shouldSuppressEventDispatchInDOM): Added. Returns true if the event is trusted,
FrameLoader::shouldSuppressKeyboardInput() returns true, and the event is a
CompositionEvent, InputEvent, KeyboardEvent, or keyboard/composition TextEvent.
(WebCore::EventDispatcher::dispatchEvent): Called stopPropogation() on the event if
shouldSuppressEventDispatchInDOM() returns true.
* dom/InputEvent.h: Removed the inline no-op destructor so that DataTransfer does not need
to be a complete type in every translation unit that includes this header. Added a type
trait specialization.
* dom/TextEvent.h: Added isKeyboard() to determine if m_inputType is TextEventInputKeyboard.
* editing/Editor.cpp:
(WebCore::Editor::shouldInsertText): Returned false if
FrameLoader::shouldSuppressKeyboardInput() returns true and the action is
EditorInsertActionTyped.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::shouldSuppressKeyboardInput): Added. Returns true if
Settings::shouldSuppressKeyboardInputDuringProvisionalNavigation() returns true and the
state is FrameStateProvisional.
* loader/FrameLoader.h: Declared shouldSuppressKeyboardInput().
* page/EventHandler.h: Exported accessKeyModifiers().
* page/Settings.in: Defined shouldSuppressKeyboardInputDuringProvisionalNavigation with an
initial value of false.
* testing/Internals.cpp:
(WebCore::Internals::accessKeyModifiers): Added. Returns a vector of strings representing
the access key modifiers.
* testing/Internals.h: Declared accessKeyModifiers().
* testing/Internals.idl: Exposed accessKeyModifiers() on the internals object.

2016-12-16 Brady Eidson <beidson@apple.com>

More SQLiteIDBCursor refactoring.
@@ -70,3 +70,5 @@ class CompositionEvent final : public UIEvent {
};

} // namespace WebCore

SPECIALIZE_TYPE_TRAITS_EVENT(CompositionEvent)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2004-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2010, 2011, 2012, 2013 Google Inc. All rights reserved.
@@ -26,15 +26,20 @@
#include "config.h"
#include "EventDispatcher.h"

#include "CompositionEvent.h"
#include "EventContext.h"
#include "EventNames.h"
#include "EventPath.h"
#include "Frame.h"
#include "FrameView.h"
#include "HTMLInputElement.h"
#include "InputEvent.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "NoEventDispatchAssertion.h"
#include "ScopedEventQueue.h"
#include "ShadowRoot.h"
#include "TextEvent.h"
#include "TouchEvent.h"

namespace WebCore {
@@ -102,6 +107,26 @@ static void dispatchEventInDOM(Event& event, const EventPath& path)
}
}

static bool shouldSuppressEventDispatchInDOM(Node& node, Event& event)
{
if (!event.isTrusted())
return false;

auto frame = node.document().frame();
if (!frame)
return false;

if (!frame->loader().shouldSuppressKeyboardInput())
return false;

if (is<TextEvent>(event)) {
auto& textEvent = downcast<TextEvent>(event);
return textEvent.isKeyboard() || textEvent.isComposition();
}

return is<CompositionEvent>(event) || is<InputEvent>(event) || is<KeyboardEvent>(event);
}

bool EventDispatcher::dispatchEvent(Node& node, Event& event)
{
ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
@@ -129,6 +154,9 @@ bool EventDispatcher::dispatchEvent(Node& node, Event& event)
if (is<HTMLInputElement>(node))
downcast<HTMLInputElement>(node).willDispatchEvent(event, clickHandlingState);

if (shouldSuppressEventDispatchInDOM(node, event))
event.stopPropagation();

if (!event.propagationStopped() && !eventPath.isEmpty()) {
event.setEventPath(eventPath);
dispatchEventInDOM(event, eventPath);
@@ -48,8 +48,6 @@ class InputEvent final : public UIEvent {
InputEvent(const AtomicString& eventType, const String& inputType, bool canBubble, bool cancelable, DOMWindow*, const String& data, RefPtr<DataTransfer>&&, const Vector<RefPtr<StaticRange>>& targetRanges, int detail);
InputEvent(const AtomicString& eventType, const Init&, IsTrusted);

virtual ~InputEvent() { }

bool isInputEvent() const override { return true; }
EventInterface eventInterface() const final { return InputEventInterfaceType; }
const String& inputType() const { return m_inputType; }
@@ -65,3 +63,5 @@ class InputEvent final : public UIEvent {
};

} // namespace WebCore

SPECIALIZE_TYPE_TRAITS_EVENT(InputEvent)
@@ -60,6 +60,7 @@ namespace WebCore {
bool isDrop() const { return m_inputType == TextEventInputDrop; }
bool isDictation() const { return m_inputType == TextEventInputDictation; }
bool isAutocompletion() const { return m_inputType == TextEventInputAutocompletion; }
bool isKeyboard() const { return m_inputType == TextEventInputKeyboard; }

bool shouldSmartReplace() const { return m_shouldSmartReplace; }
bool shouldMatchStyle() const { return m_shouldMatchStyle; }
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2008, 2011, 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2006-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
@@ -573,6 +573,9 @@ bool Editor::tryDHTMLPaste()

bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
{
if (m_frame.loader().shouldSuppressKeyboardInput() && action == EditorInsertActionTyped)
return false;

return client() && client()->shouldInsertText(text, range, action);
}

@@ -3717,4 +3717,9 @@ RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, const FrameLo
return frame;
}

bool FrameLoader::shouldSuppressKeyboardInput() const
{
return m_frame.settings().shouldSuppressKeyboardInputDuringProvisionalNavigation() && m_state == FrameStateProvisional;
}

} // namespace WebCore
@@ -299,6 +299,7 @@ class FrameLoader {
void setProvisionalLoadErrorBeingHandledURL(const URL& url) { m_provisionalLoadErrorBeingHandledURL = url; }

bool isAlwaysOnLoggingAllowed() const;
bool shouldSuppressKeyboardInput() const;

private:
enum FormSubmissionCacheLoadPolicy {
@@ -243,7 +243,7 @@ class EventHandler {

bool needsKeyboardEventDisambiguationQuirks() const;

static OptionSet<PlatformEvent::Modifier> accessKeyModifiers();
WEBCORE_EXPORT static OptionSet<PlatformEvent::Modifier> accessKeyModifiers();
WEBCORE_EXPORT bool handleAccessKey(const PlatformKeyboardEvent&);
WEBCORE_EXPORT bool keyEvent(const PlatformKeyboardEvent&);
void defaultKeyboardEventHandler(KeyboardEvent&);
@@ -292,3 +292,5 @@ largeImageAsyncDecodingEnabled initial=true
animatedImageAsyncDecodingEnabled initial=true

es6ModulesEnabled initial=false

shouldSuppressKeyboardInputDuringProvisionalNavigation initial=false
@@ -3534,5 +3534,31 @@ bool Internals::pageHasPointerLock() const
}
#endif

Vector<String> Internals::accessKeyModifiers() const
{
Vector<String> accessKeyModifierStrings;

for (auto modifier : EventHandler::accessKeyModifiers()) {
switch (modifier) {
case PlatformEvent::Modifier::AltKey:
accessKeyModifierStrings.append(ASCIILiteral("altKey"));
break;
case PlatformEvent::Modifier::CtrlKey:
accessKeyModifierStrings.append(ASCIILiteral("ctrlKey"));
break;
case PlatformEvent::Modifier::MetaKey:
accessKeyModifierStrings.append(ASCIILiteral("metaKey"));
break;
case PlatformEvent::Modifier::ShiftKey:
accessKeyModifierStrings.append(ASCIILiteral("shiftKey"));
break;
case PlatformEvent::Modifier::CapsLockKey:
accessKeyModifierStrings.append(ASCIILiteral("capsLockKey"));
break;
}
}

return accessKeyModifierStrings;
}

} // namespace WebCore

0 comments on commit bc7fb47

Please sign in to comment.