Cannot tab out of WKWebView on macOS
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jan 2019 22:53:39 +0000 (22:53 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jan 2019 22:53:39 +0000 (22:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161448
<rdar://problem/28100085>

Reviewed by Dean Jackson.

Source/WebCore/PAL:

* pal/spi/mac/NSViewSPI.h:
* pal/spi/mac/NSWindowSPI.h:
Move some SPI declarations in here from WebKitLegacy.

Source/WebKit:

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::takeFocus):
If the UIDelegate doesn't implement takeFocus, provide a simple default
implementation that just uses AppKit's key view loop to move the focus.

* UIProcess/API/APIUIClient.h:
(API::UIClient::takeFocus):
* UIProcess/API/C/WKPage.cpp:
(WKPageSetPageUIClient):
* UIProcess/Cocoa/UIDelegate.h:
* UIProcess/Cocoa/UIDelegate.mm:
(WebKit::UIDelegate::UIClient::takeFocus):
Make API::UIClient's takeFocus return a bool indicating whether the
client implements it or not.

* UIProcess/PageClient.h:
* UIProcess/mac/PageClientImplMac.h:
* UIProcess/mac/PageClientImplMac.mm:
(WebKit::PageClientImpl::takeFocus):
Plumb takeFocus to WebViewImpl.

* UIProcess/Cocoa/WebViewImpl.h:
* UIProcess/Cocoa/WebViewImpl.mm:
(WebKit::WebViewImpl::takeFocus):
Borrow the relevant portion of WebKitLegacy's implementation of takeFocus,
shifting focus to the next/previous key view in the window.

Source/WebKitLegacy/mac:

* WebCoreSupport/WebChromeClient.mm:
* WebView/WebView.mm:
Make use of SPI headers.

Tools:

Add a test that tabbing into and out of WKWebView works correctly.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/CommandBackForward.mm:
(WebKit2_CommandBackForwardTest::SetUp):
(-[CommandBackForwardOffscreenWindow isKeyWindow]): Deleted.
(-[CommandBackForwardOffscreenWindow isVisible]): Deleted.
* TestWebKitAPI/Tests/WebKitCocoa/TabOutOfWebView.mm: Added.
(-[FocusableView canBecomeKeyView]):
(TEST):
* TestWebKitAPI/mac/OffscreenWindow.h: Added.
* TestWebKitAPI/mac/OffscreenWindow.mm: Added.
(-[OffscreenWindow initWithSize:]):
(-[OffscreenWindow isKeyWindow]):
(-[OffscreenWindow isVisible]):
* TestWebKitAPI/mac/PlatformWebViewMac.mm:
(TestWebKitAPI::PlatformWebView::initialize):
(-[ActiveOffscreenWindow isKeyWindow]): Deleted.
(-[ActiveOffscreenWindow isVisible]): Deleted.
Factor ActiveOffscreenWindow out into OffscreenWindow and share it.

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

24 files changed:
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/mac/NSViewSPI.h
Source/WebCore/PAL/pal/spi/mac/NSWindowSPI.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/APIUIClient.h
Source/WebKit/UIProcess/API/C/WKPage.cpp
Source/WebKit/UIProcess/Cocoa/UIDelegate.h
Source/WebKit/UIProcess/Cocoa/UIDelegate.mm
Source/WebKit/UIProcess/Cocoa/WebViewImpl.h
Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
Source/WebKit/UIProcess/PageClient.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/mac/PageClientImplMac.h
Source/WebKit/UIProcess/mac/PageClientImplMac.mm
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm
Source/WebKitLegacy/mac/WebView/WebView.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/CommandBackForward.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/TabOutOfWebView.mm [new file with mode: 0644]
Tools/TestWebKitAPI/mac/OffscreenWindow.h [new file with mode: 0644]
Tools/TestWebKitAPI/mac/OffscreenWindow.mm [new file with mode: 0644]
Tools/TestWebKitAPI/mac/PlatformWebViewMac.mm

index 7877390..4560dd2 100644 (file)
@@ -1,3 +1,15 @@
+2019-01-15  Tim Horton  <timothy_horton@apple.com>
+
+        Cannot tab out of WKWebView on macOS
+        https://bugs.webkit.org/show_bug.cgi?id=161448
+        <rdar://problem/28100085>
+
+        Reviewed by Dean Jackson.
+
+        * pal/spi/mac/NSViewSPI.h:
+        * pal/spi/mac/NSWindowSPI.h:
+        Move some SPI declarations in here from WebKitLegacy.
+
 2019-01-14  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r239901, r239909, r239910, r239912,
index 9eedb73..d71ff6b 100644 (file)
@@ -28,6 +28,9 @@
 #import <pal/spi/cocoa/QuartzCoreSPI.h>
 
 @interface NSView () <CALayerDelegate>
+
+- (NSView *)_findLastViewInKeyViewLoop;
+
 @end
 
 #endif // PLATFORM(MAC)
index 0bb2a99..08af95a 100644 (file)
@@ -37,6 +37,7 @@
 
 @interface NSWindow ()
 
+- (id)_oldFirstResponderBeforeBecoming;
 - (id)_newFirstResponderAfterResigning;
 - (void)_setCursorForMouseLocation:(NSPoint)point;
 
index 22f7254..61aabfc 100644 (file)
@@ -1,3 +1,38 @@
+2019-01-15  Tim Horton  <timothy_horton@apple.com>
+
+        Cannot tab out of WKWebView on macOS
+        https://bugs.webkit.org/show_bug.cgi?id=161448
+        <rdar://problem/28100085>
+
+        Reviewed by Dean Jackson.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::takeFocus):
+        If the UIDelegate doesn't implement takeFocus, provide a simple default
+        implementation that just uses AppKit's key view loop to move the focus.
+
+        * UIProcess/API/APIUIClient.h:
+        (API::UIClient::takeFocus):
+        * UIProcess/API/C/WKPage.cpp:
+        (WKPageSetPageUIClient):
+        * UIProcess/Cocoa/UIDelegate.h:
+        * UIProcess/Cocoa/UIDelegate.mm:
+        (WebKit::UIDelegate::UIClient::takeFocus):
+        Make API::UIClient's takeFocus return a bool indicating whether the
+        client implements it or not.
+
+        * UIProcess/PageClient.h:
+        * UIProcess/mac/PageClientImplMac.h:
+        * UIProcess/mac/PageClientImplMac.mm:
+        (WebKit::PageClientImpl::takeFocus):
+        Plumb takeFocus to WebViewImpl.
+
+        * UIProcess/Cocoa/WebViewImpl.h:
+        * UIProcess/Cocoa/WebViewImpl.mm:
+        (WebKit::WebViewImpl::takeFocus):
+        Borrow the relevant portion of WebKitLegacy's implementation of takeFocus,
+        shifting focus to the next/previous key view in the window.
+
 2019-01-15  Youenn Fablet  <youenn@apple.com>
 
         Remove the specific handling of ephemeral sessions from CacheStorage::Engine::from
index 43ff763..08948e7 100644 (file)
@@ -81,7 +81,7 @@ public:
     virtual void hasVideoInPictureInPictureDidChange(WebKit::WebPageProxy*, bool) { }
     virtual void close(WebKit::WebPageProxy*) { }
 
-    virtual void takeFocus(WebKit::WebPageProxy*, WKFocusDirection) { }
+    virtual bool takeFocus(WebKit::WebPageProxy*, WKFocusDirection) { return false; }
     virtual void focus(WebKit::WebPageProxy*) { }
     virtual void unfocus(WebKit::WebPageProxy*) { }
 
index d4ee8cc..6b40b9f 100644 (file)
@@ -1553,12 +1553,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient
             m_client.close(toAPI(page), m_client.base.clientInfo);
         }
 
-        void takeFocus(WebPageProxy* page, WKFocusDirection direction) final
+        bool takeFocus(WebPageProxy* page, WKFocusDirection direction) final
         {
             if (!m_client.takeFocus)
-                return;
+                return false;
 
             m_client.takeFocus(toAPI(page), direction, m_client.base.clientInfo);
+            return true;
         }
 
         void focus(WebPageProxy* page) final
index 38dd7ec..6d68069 100644 (file)
@@ -95,7 +95,7 @@ private:
         void exceededDatabaseQuota(WebPageProxy*, WebFrameProxy*, API::SecurityOrigin*, const WTF::String& databaseName, const WTF::String& displayName, unsigned long long currentQuota, unsigned long long currentOriginUsage, unsigned long long currentUsage, unsigned long long expectedUsage, Function<void(unsigned long long)>&& completionHandler) final;
         void reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin&, uint64_t currentQuota, uint64_t totalBytesNeeded, Function<void(unsigned long long)>&& completionHandler) final;
         void didResignInputElementStrongPasswordAppearance(WebPageProxy&, API::Object*) final;
-        void takeFocus(WebPageProxy*, WKFocusDirection) final;
+        bool takeFocus(WebPageProxy*, WKFocusDirection) final;
 #if PLATFORM(MAC)
         void showPage(WebPageProxy*) final;
         void focus(WebPageProxy*) final;
index c16853f..daecab4 100644 (file)
@@ -466,16 +466,17 @@ static inline _WKFocusDirection toWKFocusDirection(WKFocusDirection direction)
     return _WKFocusDirectionForward;
 }
 
-void UIDelegate::UIClient::takeFocus(WebPageProxy*, WKFocusDirection direction)
+bool UIDelegate::UIClient::takeFocus(WebPageProxy*, WKFocusDirection direction)
 {
     if (!m_uiDelegate.m_delegateMethods.webViewTakeFocus)
-        return;
+        return false;
     
     auto delegate = m_uiDelegate.m_delegate.get();
     if (!delegate)
-        return;
+        return false;
     
     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView takeFocus:toWKFocusDirection(direction)];
+    return true;
 }
 
 #if PLATFORM(MAC)
index f757d87..c68dbcc 100644 (file)
@@ -32,6 +32,7 @@
 #include "WKDragDestinationAction.h"
 #include "WKLayoutMode.h"
 #include "_WKOverlayScrollbarStyle.h"
+#include <WebCore/FocusDirection.h>
 #include <WebCore/ScrollTypes.h>
 #include <WebCore/TextIndicatorWindow.h>
 #include <WebCore/UserInterfaceLayoutDirection.h>
@@ -603,6 +604,8 @@ public:
     void effectiveAppearanceDidChange();
     bool effectiveAppearanceIsDark();
 
+    void takeFocus(WebCore::FocusDirection);
+
 private:
 #if HAVE(TOUCH_BAR)
     void setUpTextTouchBar(NSTouchBar *);
index da2e32f..d51ef5d 100644 (file)
 #import <pal/spi/mac/NSScrollerImpSPI.h>
 #import <pal/spi/mac/NSSpellCheckerSPI.h>
 #import <pal/spi/mac/NSTextFinderSPI.h>
+#import <pal/spi/mac/NSViewSPI.h>
 #import <pal/spi/mac/NSWindowSPI.h>
 #import <sys/stat.h>
 #import <wtf/NeverDestroyed.h>
@@ -1570,6 +1571,20 @@ bool WebViewImpl::resignFirstResponder()
     return true;
 }
 
+void WebViewImpl::takeFocus(WebCore::FocusDirection direction)
+{
+    NSView *webView = m_view.getAutoreleased();
+
+    if (direction == FocusDirectionForward) {
+        // Since we're trying to move focus out of m_webView, and because
+        // m_webView may contain subviews within it, we ask it for the next key
+        // view of the last view in its key view loop. This makes m_webView
+        // behave as if it had no subviews, which is the behavior we want.
+        [webView.window selectKeyViewFollowingView:[webView _findLastViewInKeyViewLoop]];
+    } else
+        [webView.window selectKeyViewPrecedingView:webView];
+}
+
 void WebViewImpl::showSafeBrowsingWarning(const SafeBrowsingWarning& warning, CompletionHandler<void(Variant<ContinueUnsafeLoad, URL>&&)>&& completionHandler)
 {
     if (!m_view)
index 82c7732..7ee0c1c 100644 (file)
@@ -33,6 +33,7 @@
 #include "WebPopupMenuProxy.h"
 #include <WebCore/AlternativeTextClient.h>
 #include <WebCore/EditorClient.h>
+#include <WebCore/FocusDirection.h>
 #include <WebCore/UserInterfaceLayoutDirection.h>
 #include <WebCore/ValidationBubble.h>
 #include <wtf/CompletionHandler.h>
@@ -310,6 +311,8 @@ public:
     virtual void exitAcceleratedCompositingMode() = 0;
     virtual void updateAcceleratedCompositingMode(const LayerTreeContext&) = 0;
 
+    virtual void takeFocus(WebCore::FocusDirection) { }
+
 #if PLATFORM(MAC)
     virtual void pluginFocusOrWindowFocusChanged(uint64_t pluginComplexTextInputIdentifier, bool pluginHasFocusAndWindowHasFocus) = 0;
     virtual void setPluginComplexTextInputState(uint64_t pluginComplexTextInputIdentifier, PluginComplexTextInputState) = 0;
index b04873a..6d4dfdb 100644 (file)
@@ -5861,7 +5861,10 @@ void WebPageProxy::setFocus(bool focused)
 
 void WebPageProxy::takeFocus(uint32_t direction)
 {
-    m_uiClient->takeFocus(this, (static_cast<FocusDirection>(direction) == FocusDirectionForward) ? kWKFocusDirectionForward : kWKFocusDirectionBackward);
+    if (m_uiClient->takeFocus(this, (static_cast<FocusDirection>(direction) == FocusDirectionForward) ? kWKFocusDirectionForward : kWKFocusDirectionBackward))
+        return;
+
+    pageClient().takeFocus(static_cast<FocusDirection>(direction));
 }
 
 void WebPageProxy::setToolTip(const String& toolTip)
index 7cfdc57..12dc25a 100644 (file)
@@ -262,6 +262,8 @@ private:
     void didRestoreScrollPosition() override;
     bool windowIsFrontWindowUnderMouse(const NativeWebMouseEvent&) override;
 
+    void takeFocus(WebCore::FocusDirection) override;
+
     NSView *m_view;
     WeakPtr<WebViewImpl> m_impl;
 #if USE(AUTOCORRECTION_PANEL)
index a0cefdd..5d7c238 100644 (file)
@@ -957,6 +957,11 @@ bool PageClientImpl::effectiveAppearanceIsDark() const
     return m_impl->effectiveAppearanceIsDark();
 }
 
+void PageClientImpl::takeFocus(WebCore::FocusDirection direction)
+{
+    m_impl->takeFocus(direction);
+}
+
 } // namespace WebKit
 
 #endif // PLATFORM(MAC)
index 41e0d31..a3ad631 100644 (file)
@@ -1,3 +1,15 @@
+2019-01-15  Tim Horton  <timothy_horton@apple.com>
+
+        Cannot tab out of WKWebView on macOS
+        https://bugs.webkit.org/show_bug.cgi?id=161448
+        <rdar://problem/28100085>
+
+        Reviewed by Dean Jackson.
+
+        * WebCoreSupport/WebChromeClient.mm:
+        * WebView/WebView.mm:
+        Make use of SPI headers.
+
 2019-01-14  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r239901, r239909, r239910, r239912,
index 4c5d18d..3e0a5c2 100644 (file)
@@ -85,6 +85,7 @@
 #import <WebCore/SerializedCryptoKeyWrap.h>
 #import <WebCore/Widget.h>
 #import <WebCore/WindowFeatures.h>
+#import <pal/spi/mac/NSViewSPI.h>
 #import <wtf/BlockObjCExceptions.h>
 #import <wtf/RefPtr.h>
 #import <wtf/Vector.h>
@@ -135,10 +136,6 @@ NSString *WebConsoleMessageErrorMessageLevel = @"ErrorMessageLevel";
 @end
 #endif
 
-@interface NSView (WebNSViewDetails)
-- (NSView *)_findLastViewInKeyViewLoop;
-@end
-
 // For compatibility with old SPI.
 @interface NSView (WebOldWebKitPlugInDetails)
 - (void)setIsSelected:(BOOL)isSelected;
index ab83845..a5e4118 100644 (file)
@@ -375,7 +375,6 @@ SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
 @end
 
 @interface NSWindow (WebNSWindowDetails)
-- (id)_oldFirstResponderBeforeBecoming;
 - (void)_enableScreenUpdatesIfNeeded;
 - (BOOL)_wrapsCarbonWindow;
 - (BOOL)_hasKeyAppearance;
index 1300f79..b6ceeb9 100644 (file)
@@ -1,3 +1,32 @@
+2019-01-15  Tim Horton  <timothy_horton@apple.com>
+
+        Cannot tab out of WKWebView on macOS
+        https://bugs.webkit.org/show_bug.cgi?id=161448
+        <rdar://problem/28100085>
+
+        Reviewed by Dean Jackson.
+
+        Add a test that tabbing into and out of WKWebView works correctly.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/CommandBackForward.mm:
+        (WebKit2_CommandBackForwardTest::SetUp):
+        (-[CommandBackForwardOffscreenWindow isKeyWindow]): Deleted.
+        (-[CommandBackForwardOffscreenWindow isVisible]): Deleted.
+        * TestWebKitAPI/Tests/WebKitCocoa/TabOutOfWebView.mm: Added.
+        (-[FocusableView canBecomeKeyView]):
+        (TEST):
+        * TestWebKitAPI/mac/OffscreenWindow.h: Added.
+        * TestWebKitAPI/mac/OffscreenWindow.mm: Added.
+        (-[OffscreenWindow initWithSize:]):
+        (-[OffscreenWindow isKeyWindow]):
+        (-[OffscreenWindow isVisible]):
+        * TestWebKitAPI/mac/PlatformWebViewMac.mm:
+        (TestWebKitAPI::PlatformWebView::initialize):
+        (-[ActiveOffscreenWindow isKeyWindow]): Deleted.
+        (-[ActiveOffscreenWindow isVisible]): Deleted.
+        Factor ActiveOffscreenWindow out into OffscreenWindow and share it.
+
 2019-01-15  Ryan Haddad  <ryanhaddad@apple.com>
 
         Adjust the arguments passed to run-javascriptcore-tests for the 32-bit JSC bot
index ccdd8ee..7a1072a 100644 (file)
@@ -90,6 +90,8 @@
                2D21FE591F04642900B58E7D /* WKPDFViewStablePresentationUpdateCallback.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D21FE581F04642800B58E7D /* WKPDFViewStablePresentationUpdateCallback.mm */; };
                2D4CF8BD1D8360CC0001CE8D /* WKThumbnailView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D4CF8BC1D8360CC0001CE8D /* WKThumbnailView.mm */; };
                2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D51A0C51C8BF00400765C45 /* DOMHTMLVideoElementWrapper.mm */; };
+               2D70059621EDA0C6003463CB /* TabOutOfWebView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */; };
+               2D70059921EDA4D0003463CB /* OffscreenWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059721EDA4D0003463CB /* OffscreenWindow.mm */; };
                2D838B1F1EEF3A5C009B980E /* WKContentViewEditingActions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */; };
                2DADF26321CB8F32003D3E3A /* GetResourceData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DADF26221CB8F32003D3E3A /* GetResourceData.mm */; };
                2DB0232F1E4E871800707123 /* InteractionDeadlockAfterCrash.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0232E1E4E871800707123 /* InteractionDeadlockAfterCrash.mm */; };
                2D51A0C51C8BF00400765C45 /* DOMHTMLVideoElementWrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMHTMLVideoElementWrapper.mm; sourceTree = "<group>"; };
                2D61EC3021B0B75C00A7D1CB /* PencilKitTestSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PencilKitTestSPI.h; sourceTree = "<group>"; };
                2D640B5417875DFF00BFAF99 /* ScrollPinningBehaviors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollPinningBehaviors.cpp; sourceTree = "<group>"; };
+               2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TabOutOfWebView.mm; sourceTree = "<group>"; };
+               2D70059721EDA4D0003463CB /* OffscreenWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OffscreenWindow.mm; sourceTree = "<group>"; };
+               2D70059821EDA4D0003463CB /* OffscreenWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OffscreenWindow.h; sourceTree = "<group>"; };
                2D8104CB1BEC13E70020DA46 /* FindInPage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FindInPage.mm; sourceTree = "<group>"; };
                2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKContentViewEditingActions.mm; sourceTree = "<group>"; };
                2D9A53AE1B31FA8D0074D5AA /* ShrinkToFit.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ShrinkToFit.mm; sourceTree = "<group>"; };
                                CDC0932D21C993440030C4B0 /* StopSuspendResumeAllMedia.mm */,
                                515BE1701D428BD100DD7C68 /* StoreBlobThenDelete.mm */,
                                1C734B5220788C4800F430EA /* SystemColors.mm */,
+                               2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */,
                                F4CD74C720FDB49600DE3794 /* TestURLSchemeHandler.h */,
                                F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */,
                                5CB40B4D1F4B98BE007DC7B9 /* UIDelegate.mm */,
                                2E7765CE16C4D81100BA2BB1 /* mainMac.mm */,
                                F442851B2140DF2900CCDA22 /* NSFontPanelTesting.h */,
                                F442851C2140DF2900CCDA22 /* NSFontPanelTesting.mm */,
+                               2D70059821EDA4D0003463CB /* OffscreenWindow.h */,
+                               2D70059721EDA4D0003463CB /* OffscreenWindow.mm */,
                                BC131884117114B600B69727 /* PlatformUtilitiesMac.mm */,
                                BC90955C125548AA00083756 /* PlatformWebViewMac.mm */,
                                C081224313FC19EC00DC39AE /* SyntheticBackingScaleFactorWindow.h */,
                                A10F047E1E3AD29C00C95E19 /* NSFileManagerExtras.mm in Sources */,
                                F442851D2140DF2900CCDA22 /* NSFontPanelTesting.mm in Sources */,
                                37A22AA71DCAA27200AFBFC4 /* ObservedRenderingProgressEventsAfterCrash.mm in Sources */,
+                               2D70059921EDA4D0003463CB /* OffscreenWindow.mm in Sources */,
                                7CCE7F251A411AF600447C4C /* OpenAndCloseWindow.mm in Sources */,
                                CEBCA12F1E3A660100C73293 /* OverrideContentSecurityPolicy.mm in Sources */,
                                7CCB4DA91C83AE7300CC6918 /* PageGroup.cpp in Sources */,
                                4433A396208044140091ED57 /* SynchronousTimeoutTests.mm in Sources */,
                                7CCE7EA81A411A1900447C4C /* SyntheticBackingScaleFactorWindow.m in Sources */,
                                1C734B5320788C4800F430EA /* SystemColors.mm in Sources */,
+                               2D70059621EDA0C6003463CB /* TabOutOfWebView.mm in Sources */,
                                7CCE7F161A411AE600447C4C /* TerminateTwice.cpp in Sources */,
                                7CCE7EA91A411A1D00447C4C /* TestBrowsingContextLoadDelegate.mm in Sources */,
                                F46128CB211D475100D9FADB /* TestDraggingInfo.mm in Sources */,
index b3c5af9..a48c783 100644 (file)
@@ -27,6 +27,7 @@
 
 #if PLATFORM(MAC)
 
+#import "OffscreenWindow.h"
 #import "PlatformUtilities.h"
 #import "Test.h"
 #import "TestNavigationDelegate.h"
 #import <wtf/RetainPtr.h>
 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
 
-@interface CommandBackForwardOffscreenWindow : NSWindow
-@end
-
-@implementation CommandBackForwardOffscreenWindow
-- (BOOL)isKeyWindow
-{
-    return YES;
-}
-- (BOOL)isVisible
-{
-    return YES;
-}
-@end
-
 enum ArrowDirection {
     Left,
     Right
@@ -96,11 +83,7 @@ public:
 
     virtual void SetUp()
     {
-        NSWindow *window = [[CommandBackForwardOffscreenWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
-        [window setColorSpace:[[NSScreen mainScreen] colorSpace]];
-        [window orderBack:nil];
-        [window setAutodisplay:NO];
-        [window setReleasedWhenClosed:NO];
+        window = adoptNS([[OffscreenWindow alloc] initWithSize:CGSizeMake(100, 100)]);
     }
 };
 
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/TabOutOfWebView.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/TabOutOfWebView.mm
new file mode 100644 (file)
index 0000000..0bc2f9e
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 Apple Inc. 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.
+ *
+ * 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"
+
+#if WK_API_ENABLED && PLATFORM(MAC)
+
+#import "OffscreenWindow.h"
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <Carbon/Carbon.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <wtf/RetainPtr.h>
+
+enum class TabDirection : uint8_t {
+    Forward,
+    Backward,
+};
+
+@interface NSApplication ()
+- (void)_setCurrentEvent:(NSEvent *)event;
+@end
+
+@interface FocusableView : NSView
+@end
+
+@implementation FocusableView
+
+- (BOOL)canBecomeKeyView
+{
+    return YES;
+}
+
+@end
+
+TEST(WebKit, TabOutOfWebView)
+{
+    RetainPtr<FocusableView> beforeView = adoptNS([[FocusableView alloc] initWithFrame:NSMakeRect(0, 200, 100, 100)]);
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 100, 100, 100) configuration:configuration.get() addToWindow:NO]);
+    RetainPtr<FocusableView> afterView = adoptNS([[FocusableView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
+    RetainPtr<OffscreenWindow> window = adoptNS([[OffscreenWindow alloc] initWithSize:CGSizeMake(800, 600)]);
+
+    [[window contentView] addSubview:beforeView.get()];
+    [[window contentView] addSubview:webView.get()];
+    [[window contentView] addSubview:afterView.get()];
+
+    [beforeView setNextKeyView:webView.get()];
+    [webView setNextKeyView:afterView.get()];
+    [afterView setNextKeyView:beforeView.get()];
+
+    auto simulateTabKeyPress = ^(TabDirection direction) {
+        NSString *characters;
+        unsigned short keyCode = kVK_Tab;
+        NSEventModifierFlags flags = 0;
+
+        switch (direction) {
+        case TabDirection::Forward:
+            characters = @"\t";
+            break;
+        case TabDirection::Backward:
+            characters = @"\x0019";
+            flags = NSEventModifierFlagShift;
+            break;
+        }
+
+        NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(5, 5) modifierFlags:flags timestamp:GetCurrentEventTime() windowNumber:[window windowNumber] context:[NSGraphicsContext currentContext] characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
+
+        [NSApp _setCurrentEvent:event];
+        [[window firstResponder] keyDown:event];
+        [NSApp _setCurrentEvent:nil];
+
+        event = [NSEvent keyEventWithType:NSEventTypeKeyUp location:NSMakePoint(5, 5) modifierFlags:flags timestamp:GetCurrentEventTime() windowNumber:[window windowNumber] context:[NSGraphicsContext currentContext] characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
+
+        [NSApp _setCurrentEvent:event];
+        [[window firstResponder] keyUp:event];
+        [NSApp _setCurrentEvent:nil];
+
+        // Bounce through JavaScript so that asynchronous
+        // web content focus changes have definitely taken effect.
+        [webView stringByEvaluatingJavaScript:@""];
+    };
+
+    [webView synchronouslyLoadHTMLString:@"<input id='one'><input id='two'>"];
+
+    // Focus the first view.
+    [window makeFirstResponder:beforeView.get()];
+    EXPECT_EQ([window firstResponder], beforeView.get());
+
+    // Tab into the web view, which will focus the first input.
+    simulateTabKeyPress(TabDirection::Forward);
+    EXPECT_EQ([window firstResponder], webView.get());
+    EXPECT_WK_STREQ("one", [webView stringByEvaluatingJavaScript:@"document.activeElement.id"]);
+
+    // Tab from the first input to the second input.
+    simulateTabKeyPress(TabDirection::Forward);
+    EXPECT_EQ([window firstResponder], webView.get());
+    EXPECT_WK_STREQ("two", [webView stringByEvaluatingJavaScript:@"document.activeElement.id"]);
+
+    // Tab out of the web view.
+    simulateTabKeyPress(TabDirection::Forward);
+    EXPECT_EQ([window firstResponder], afterView.get());
+
+    // Reverse-tab back into the web view.
+    simulateTabKeyPress(TabDirection::Backward);
+    EXPECT_EQ([window firstResponder], webView.get());
+    EXPECT_WK_STREQ("two", [webView stringByEvaluatingJavaScript:@"document.activeElement.id"]);
+
+    // Reverse-tab from the second input to the first input.
+    simulateTabKeyPress(TabDirection::Backward);
+    EXPECT_WK_STREQ("one", [webView stringByEvaluatingJavaScript:@"document.activeElement.id"]);
+
+    // Reverse-tab back out of the web view to the first view.
+    simulateTabKeyPress(TabDirection::Backward);
+    EXPECT_EQ([window firstResponder], beforeView.get());
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/mac/OffscreenWindow.h b/Tools/TestWebKitAPI/mac/OffscreenWindow.h
new file mode 100644 (file)
index 0000000..e4efe02
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 Apple Inc. 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.
+ *
+ * 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 PLATFORM(MAC)
+
+#import <AppKit/AppKit.h>
+
+@interface OffscreenWindow : NSWindow
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag NS_UNAVAILABLE;
+- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag screen:(NSScreen *)screen NS_UNAVAILABLE;
+
+- (instancetype)initWithSize:(CGSize)size;
+
+@end
+
+#endif
diff --git a/Tools/TestWebKitAPI/mac/OffscreenWindow.mm b/Tools/TestWebKitAPI/mac/OffscreenWindow.mm
new file mode 100644 (file)
index 0000000..7a100f7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 Apple Inc. 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.
+ *
+ * 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.
+ */
+
+#import "config.h"
+#import "OffscreenWindow.h"
+
+@implementation OffscreenWindow
+
+- (instancetype)initWithSize:(CGSize)size
+{
+    NSRect rect = NSMakeRect(0, 0, size.width, size.height);
+    NSRect windowRect = NSOffsetRect(rect, -10000, [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
+    self = [super initWithContentRect:windowRect styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES];
+    if (!self)
+        return nil;
+
+    self.colorSpace = [[NSScreen mainScreen] colorSpace];
+    [self orderBack:nil];
+    self.autodisplay = NO;
+    self.releasedWhenClosed = NO;
+
+    return self;
+}
+
+- (BOOL)isKeyWindow
+{
+    return YES;
+}
+
+- (BOOL)isVisible
+{
+    return YES;
+}
+
+@end
index cb1826d..40339de 100644 (file)
 #import "config.h"
 #import "PlatformWebView.h"
 
+#import "OffscreenWindow.h"
 #import <Carbon/Carbon.h>
 #import <WebKit/WKRetainPtr.h>
 #import <WebKit/WKViewPrivate.h>
 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
 
-@interface ActiveOffscreenWindow : NSWindow
-@end
-
-@implementation ActiveOffscreenWindow
-- (BOOL)isKeyWindow
-{
-    return YES;
-}
-- (BOOL)isVisible
-{
-    return YES;
-}
-@end
-
 namespace TestWebKitAPI {
 
 void PlatformWebView::initialize(WKPageConfigurationRef configuration, Class wkViewSubclass)
@@ -53,13 +40,8 @@ void PlatformWebView::initialize(WKPageConfigurationRef configuration, Class wkV
     m_view = [[wkViewSubclass alloc] initWithFrame:rect configurationRef:configuration];
     [m_view setWindowOcclusionDetectionEnabled:NO];
 
-    NSRect windowRect = NSOffsetRect(rect, -10000, [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000);
-    m_window = [[ActiveOffscreenWindow alloc] initWithContentRect:windowRect styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES];
-    [m_window setColorSpace:[[NSScreen mainScreen] colorSpace]];
+    m_window = [[OffscreenWindow alloc] initWithSize:NSSizeToCGSize(rect.size)];
     [[m_window contentView] addSubview:m_view];
-    [m_window orderBack:nil];
-    [m_window setAutodisplay:NO];
-    [m_window setReleasedWhenClosed:NO];
 }
 
 PlatformWebView::PlatformWebView(WKPageConfigurationRef configuration)