Diff
Modified: trunk/Source/WebCore/ChangeLog (257721 => 257722)
--- trunk/Source/WebCore/ChangeLog 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/ChangeLog 2020-03-02 19:54:43 UTC (rev 257722)
@@ -1,3 +1,28 @@
+2020-03-02 Daniel Bates <[email protected]>
+
+ Page::editableElementsInRect() should find nested editable elements and return found elements in paint order
+ https://bugs.webkit.org/show_bug.cgi?id=208352
+ <rdar://problem/59867815>
+
+ Reviewed by Wenson Hsieh.
+
+ Use the existing rect-based hit testing machinery to collect all the editable elements on
+ the page that a person can hit. This makes it possible to find nested editable elements
+ (e.g. an <input> inside a <div contenteditable="true">), ignore elements with CSS "pointer-events: none",
+ and return elements in paint order (closest to farthest to a person's face). The latter
+ makes it possible for a caller to know what element is frontmost, especially when two
+ editable elements overlap.
+
+ * page/Page.cpp:
+ (WebCore::Page::editableElementsInRect const): Implement in terms of hit testing machinery.
+ * rendering/HitTestLocation.cpp:
+ (WebCore::HitTestLocation::HitTestLocation):
+ * rendering/HitTestLocation.h:
+ * rendering/HitTestResult.cpp:
+ (WebCore::HitTestResult::HitTestResult):
+ * rendering/HitTestResult.h:
+ Added convenience constructors that take a LayoutRect.
+
2020-03-02 Doug Kelly <[email protected]>
ASSERT(m_column != unsetColumnIndex) in RenderTable::cellBefore
Modified: trunk/Source/WebCore/page/Page.cpp (257721 => 257722)
--- trunk/Source/WebCore/page/Page.cpp 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/page/Page.cpp 2020-03-02 19:54:43 UTC (rev 257722)
@@ -128,7 +128,6 @@
#include "VoidCallback.h"
#include "WheelEventDeltaFilter.h"
#include "Widget.h"
-#include <wtf/Deque.h>
#include <wtf/FileSystem.h>
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
@@ -927,26 +926,29 @@
Vector<Ref<Element>> Page::editableElementsInRect(const FloatRect& searchRectInRootViewCoordinates) const
{
- Vector<Ref<Element>> result;
- forEachDocument([&] (Document& document) {
- Deque<Node*> nodesToSearch;
- nodesToSearch.append(&document);
- while (!nodesToSearch.isEmpty()) {
- auto* node = nodesToSearch.takeFirst();
+ auto frameView = makeRefPtr(mainFrame().view());
+ if (!frameView)
+ return { };
- // It is possible to have nested text input contexts (e.g. <input type='text'> inside contenteditable) but
- // in this case we just take the outermost context and skip the rest.
- if (!is<Element>(node) || !isEditableTextInputElement(downcast<Element>(*node))) {
- for (auto* child = node->firstChild(); child; child = child->nextSibling())
- nodesToSearch.append(child);
- continue;
- }
+ auto document = makeRefPtr(mainFrame().document());
+ if (!document)
+ return { };
- auto& element = downcast<Element>(*node);
- if (searchRectInRootViewCoordinates.intersects(element.clientRect()))
- result.append(element);
+ constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::CollectMultipleElements, HitTestRequest::DisallowUserAgentShadowContent, HitTestRequest::AllowVisibleChildFrameContentOnly };
+ LayoutRect searchRectInMainFrameCoordinates = frameView->rootViewToContents(roundedIntRect(searchRectInRootViewCoordinates));
+ HitTestResult hitTestResult { searchRectInMainFrameCoordinates };
+ if (!document->hitTest(hitType, hitTestResult))
+ return { };
+
+ Vector<Ref<Element>> result;
+ auto& nodeSet = hitTestResult.listBasedTestResult();
+ result.reserveInitialCapacity(nodeSet.size());
+ for (auto& node : nodeSet) {
+ if (is<Element>(node) && isEditableTextInputElement(downcast<Element>(*node))) {
+ ASSERT(searchRectInRootViewCoordinates.intersects(downcast<Element>(*node).clientRect()));
+ result.uncheckedAppend(downcast<Element>(*node));
}
- });
+ }
return result;
}
Modified: trunk/Source/WebCore/rendering/HitTestLocation.cpp (257721 => 257722)
--- trunk/Source/WebCore/rendering/HitTestLocation.cpp 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/rendering/HitTestLocation.cpp 2020-03-02 19:54:43 UTC (rev 257722)
@@ -48,6 +48,15 @@
{
}
+HitTestLocation::HitTestLocation(const LayoutRect& rect)
+ : m_point { rect.center() } // Rounded to an integer point; it may not be the exact center.
+ , m_boundingBox { rect }
+ , m_transformedPoint { rect.center() }
+ , m_transformedRect { FloatQuad { m_boundingBox } }
+ , m_isRectBased { true }
+{
+}
+
HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
: m_point(centerPoint)
, m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding))
Modified: trunk/Source/WebCore/rendering/HitTestLocation.h (257721 => 257722)
--- trunk/Source/WebCore/rendering/HitTestLocation.h 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/rendering/HitTestLocation.h 2020-03-02 19:54:43 UTC (rev 257722)
@@ -31,8 +31,10 @@
HitTestLocation(const LayoutPoint&);
WEBCORE_EXPORT HitTestLocation(const FloatPoint&);
HitTestLocation(const FloatPoint&, const FloatQuad&);
- // Pass non-zero padding values to perform a rect-based hit test.
+
+ HitTestLocation(const LayoutRect&);
HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
+
// Make a copy the HitTestLocation in a new region by applying given offset to internal point and area.
HitTestLocation(const HitTestLocation&, const LayoutSize& offset);
WEBCORE_EXPORT HitTestLocation(const HitTestLocation&);
Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (257721 => 257722)
--- trunk/Source/WebCore/rendering/HitTestResult.cpp 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp 2020-03-02 19:54:43 UTC (rev 257722)
@@ -62,6 +62,12 @@
{
}
+HitTestResult::HitTestResult(const LayoutRect& rect)
+ : m_hitTestLocation { rect }
+ , m_pointInInnerNodeFrame { rect.center() }
+{
+}
+
HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
: m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)
, m_pointInInnerNodeFrame(centerPoint)
Modified: trunk/Source/WebCore/rendering/HitTestResult.h (257721 => 257722)
--- trunk/Source/WebCore/rendering/HitTestResult.h 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Source/WebCore/rendering/HitTestResult.h 2020-03-02 19:54:43 UTC (rev 257722)
@@ -44,8 +44,10 @@
WEBCORE_EXPORT HitTestResult();
WEBCORE_EXPORT explicit HitTestResult(const LayoutPoint&);
- // Pass non-negative padding values to perform a rect-based hit test.
+
+ WEBCORE_EXPORT explicit HitTestResult(const LayoutRect&);
WEBCORE_EXPORT HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
+
WEBCORE_EXPORT explicit HitTestResult(const HitTestLocation&);
WEBCORE_EXPORT HitTestResult(const HitTestResult&);
WEBCORE_EXPORT ~HitTestResult();
Modified: trunk/Tools/ChangeLog (257721 => 257722)
--- trunk/Tools/ChangeLog 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Tools/ChangeLog 2020-03-02 19:54:43 UTC (rev 257722)
@@ -1,3 +1,20 @@
+2020-03-02 Daniel Bates <[email protected]>
+
+ Page::editableElementsInRect() should find nested editable elements and return found elements in paint order
+ https://bugs.webkit.org/show_bug.cgi?id=208352
+ <rdar://problem/59867815>
+
+ Reviewed by Wenson Hsieh.
+
+ Update test results to reflect the new behavior. To do this I also need to fix up the test code
+ to actually scroll the web content. This also means we now execute the same code on both Mac and
+ iOS to do the scrolling: window.scrollTo(0, 5000). Also fixed up sub-test "Inputs scrolled outside
+ the requested rect; should not be included." to use a 10000px height <div> so that you can scroll
+ to a y position of 5000. Otherwise, it's impossible to do because the page height is < 5000px.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
+ (TEST):
+
2020-03-02 Jacob Uphoff <[email protected]>
Unreviewed, rolling out r257687.
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm (257721 => 257722)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm 2020-03-02 19:46:55 UTC (rev 257721)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm 2020-03-02 19:54:43 UTC (rev 257722)
@@ -76,7 +76,6 @@
TEST(WebKit, RequestTextInputContext)
{
RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
- WKPreferencesSetThreadedScrollingEnabled((WKPreferencesRef)[configuration preferences], false);
RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
NSArray<_WKTextInputContext *> *contexts;
@@ -133,12 +132,10 @@
// Inputs scrolled outside the requested rect; should not be included.
- [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' style='width: 50px; height: 50px;'><br><div style='width: 100px; height: 5000px;'></div>")];
-#if PLATFORM(MAC)
- [webView objectByEvaluatingJavaScript:@"window.scrollTo(0, 5000);"];
-#else
- [webView scrollView].contentOffset = CGPointMake(0, 5000);
-#endif
+ [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' style='width: 50px; height: 50px;'><br><div style='width: 100px; height: 10000px;'></div>")];
+ [webView stringByEvaluatingJavaScript:@"window.scrollTo(0, 5000)"];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_EQ(5000, [[webView objectByEvaluatingJavaScript:@"pageYOffset"] floatValue]);
contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView frame]];
EXPECT_EQ(0UL, contexts.count);
@@ -145,11 +142,9 @@
// Inputs scrolled into the requested rect.
[webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' style='width: 50px; height: 50px; position: absolute; top: 5000px;'><br><div style='width: 100px; height: 10000px;'></div>")];
-#if PLATFORM(MAC)
- [webView objectByEvaluatingJavaScript:@"window.scrollTo(0, 5000);"];
-#else
- [webView scrollView].contentOffset = CGPointMake(0, 5000);
-#endif
+ [webView stringByEvaluatingJavaScript:@"window.scrollTo(0, 5000)"];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_EQ(5000, [[webView objectByEvaluatingJavaScript:@"pageYOffset"] floatValue]);
contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView frame]];
EXPECT_EQ(1UL, contexts.count);
EXPECT_EQ(CGRectMake(0, 0, 50, 50), contexts[0].boundingRect);
@@ -159,16 +154,17 @@
[webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' style='width: 50px; height: 50px;'><br/><input type='text' style='width: 50px; height: 50px;'><br/><input type='text' style='width: 50px; height: 50px;'>")];
contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView frame]];
EXPECT_EQ(3UL, contexts.count);
- EXPECT_EQ(CGRectMake(0, 0, 50, 50), contexts[0].boundingRect);
+ EXPECT_EQ(CGRectMake(0, 100, 50, 50), contexts[0].boundingRect);
EXPECT_EQ(CGRectMake(0, 50, 50, 50), contexts[1].boundingRect);
- EXPECT_EQ(CGRectMake(0, 100, 50, 50), contexts[2].boundingRect);
+ EXPECT_EQ(CGRectMake(0, 0, 50, 50), contexts[2].boundingRect);
- // Nested <input>-inside-contenteditable. Only the contenteditable is considered.
+ // Nested <input>-inside-contenteditable.
[webView synchronouslyLoadHTMLString:applyStyle(@"<div contenteditable style='width: 100px; height: 100px;'><input type='text' style='width: 50px; height: 50px;'></div>")];
contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView frame]];
- EXPECT_EQ(1UL, contexts.count);
- EXPECT_EQ(CGRectMake(0, 0, 100, 100), contexts[0].boundingRect);
+ EXPECT_EQ(2UL, contexts.count);
+ EXPECT_EQ(CGRectMake(0, 0, 50, 50), contexts[0].boundingRect);
+ EXPECT_EQ(CGRectMake(0, 0, 100, 100), contexts[1].boundingRect);
}
TEST(WebKit, DISABLED_FocusTextInputContext)