- Revision
- 257939
- Author
- [email protected]
- Date
- 2020-03-05 12:44:13 -0800 (Thu, 05 Mar 2020)
Log Message
Page-specific UserStyleSheets should wait until the initial empty document has been removed to be injected
https://bugs.webkit.org/show_bug.cgi?id=208644
<rdar://problem/60042429>
Patch by Antoine Quint <[email protected]> on 2020-03-05
Reviewed by Brady Eidson.
Source/WebCore:
When a WKWebView is created and asked to load a URL, it will first load an empty initial document (about:blank) prior
to creating a Document for the requested URL. We need to ensure that page-specific UserStyleSheets are only injected
once the Document for the requested URL starts loading.
When Page::injectUserStyleSheet() is called, if we determine that the empty initial document is visible, we enqueue
the provided UserStyleSheet and wait until the new mainFrameDidChangeToNonInitialEmptyDocument() method is called
to empty that queue and inject the UserStyleSheets then.
This new method is called from Frame::setDocument() for a main frame once we've determined the frame's document has
changed to a non-null value and that the frame loader's state machine indicates that we're no longer displaying the
initial empty document.
* dom/ExtensionStyleSheets.h:
* page/Frame.cpp:
(WebCore::Frame::setDocument):
* page/Page.cpp:
(WebCore::Page::injectUserStyleSheet):
(WebCore::Page::removeInjectedUserStyleSheet):
(WebCore::Page::mainFrameDidChangeToNonInitialEmptyDocument):
* page/Page.h:
Tools:
Add new tests for -[_WKUserStyleSheet initWithSource:forWKWebView:forMainFrameOnly:userContentWorld:]) that check:
1. that a _WKUserStyleSheet can be added immediately after a WKWebView's creation and will be injected once the initial
empty document has been removed in favor of the document for the requested URL,
2. that removing a _WKUserStyleSheet immediately after it was added but before the initial empty document was removed
correctly does not injected the style sheet.
* TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (257938 => 257939)
--- trunk/Source/WebCore/ChangeLog 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Source/WebCore/ChangeLog 2020-03-05 20:44:13 UTC (rev 257939)
@@ -1,3 +1,32 @@
+2020-03-05 Antoine Quint <[email protected]>
+
+ Page-specific UserStyleSheets should wait until the initial empty document has been removed to be injected
+ https://bugs.webkit.org/show_bug.cgi?id=208644
+ <rdar://problem/60042429>
+
+ Reviewed by Brady Eidson.
+
+ When a WKWebView is created and asked to load a URL, it will first load an empty initial document (about:blank) prior
+ to creating a Document for the requested URL. We need to ensure that page-specific UserStyleSheets are only injected
+ once the Document for the requested URL starts loading.
+
+ When Page::injectUserStyleSheet() is called, if we determine that the empty initial document is visible, we enqueue
+ the provided UserStyleSheet and wait until the new mainFrameDidChangeToNonInitialEmptyDocument() method is called
+ to empty that queue and inject the UserStyleSheets then.
+
+ This new method is called from Frame::setDocument() for a main frame once we've determined the frame's document has
+ changed to a non-null value and that the frame loader's state machine indicates that we're no longer displaying the
+ initial empty document.
+
+ * dom/ExtensionStyleSheets.h:
+ * page/Frame.cpp:
+ (WebCore::Frame::setDocument):
+ * page/Page.cpp:
+ (WebCore::Page::injectUserStyleSheet):
+ (WebCore::Page::removeInjectedUserStyleSheet):
+ (WebCore::Page::mainFrameDidChangeToNonInitialEmptyDocument):
+ * page/Page.h:
+
2020-03-05 Simon Fraser <[email protected]>
Generate layer event regions for async overflow scrolling on macOS
Modified: trunk/Source/WebCore/dom/ExtensionStyleSheets.h (257938 => 257939)
--- trunk/Source/WebCore/dom/ExtensionStyleSheets.h 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Source/WebCore/dom/ExtensionStyleSheets.h 2020-03-05 20:44:13 UTC (rev 257939)
@@ -75,6 +75,7 @@
void injectPageSpecificUserStyleSheet(const UserStyleSheet&);
void removePageSpecificUserStyleSheet(const UserStyleSheet&);
+
String contentForInjectedStyleSheet(const RefPtr<CSSStyleSheet>&) const;
void detachFromDocument();
Modified: trunk/Source/WebCore/page/Frame.cpp (257938 => 257939)
--- trunk/Source/WebCore/page/Frame.cpp 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Source/WebCore/page/Frame.cpp 2020-03-05 20:44:13 UTC (rev 257939)
@@ -294,6 +294,9 @@
}
#endif
+ if (m_page && m_doc && isMainFrame() && !loader().stateMachine().isDisplayingInitialEmptyDocument())
+ m_page->mainFrameDidChangeToNonInitialEmptyDocument();
+
InspectorInstrumentation::frameDocumentUpdated(*this);
m_documentIsBeingReplaced = false;
Modified: trunk/Source/WebCore/page/Page.cpp (257938 => 257939)
--- trunk/Source/WebCore/page/Page.cpp 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Source/WebCore/page/Page.cpp 2020-03-05 20:44:13 UTC (rev 257939)
@@ -3018,6 +3018,12 @@
void Page::injectUserStyleSheet(UserStyleSheet& userStyleSheet)
{
+ // We need to wait until we're no longer displaying the initial empty document before we can inject the stylesheets.
+ if (m_mainFrame->loader().stateMachine().isDisplayingInitialEmptyDocument()) {
+ m_userStyleSheetsPendingInjection.append(userStyleSheet);
+ return;
+ }
+
if (userStyleSheet.injectedFrames() == InjectInTopFrameOnly) {
if (auto* document = m_mainFrame->document())
document->extensionStyleSheets().injectPageSpecificUserStyleSheet(userStyleSheet);
@@ -3030,6 +3036,13 @@
void Page::removeInjectedUserStyleSheet(UserStyleSheet& userStyleSheet)
{
+ if (!m_userStyleSheetsPendingInjection.isEmpty()) {
+ m_userStyleSheetsPendingInjection.removeFirstMatching([userStyleSheet](auto& storedUserStyleSheet) {
+ return storedUserStyleSheet.url() == userStyleSheet.url();
+ });
+ return;
+ }
+
if (userStyleSheet.injectedFrames() == InjectInTopFrameOnly) {
if (auto* document = m_mainFrame->document())
document->extensionStyleSheets().removePageSpecificUserStyleSheet(userStyleSheet);
@@ -3040,4 +3053,12 @@
}
}
+void Page::mainFrameDidChangeToNonInitialEmptyDocument()
+{
+ ASSERT(!m_mainFrame->loader().stateMachine().isDisplayingInitialEmptyDocument());
+ for (auto& userStyleSheet : m_userStyleSheetsPendingInjection)
+ injectUserStyleSheet(userStyleSheet);
+ m_userStyleSheetsPendingInjection.clear();
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/page/Page.h (257938 => 257939)
--- trunk/Source/WebCore/page/Page.h 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Source/WebCore/page/Page.h 2020-03-05 20:44:13 UTC (rev 257939)
@@ -255,6 +255,7 @@
bool shouldEnableICECandidateFilteringByDefault() const { return m_shouldEnableICECandidateFilteringByDefault; }
void didChangeMainDocument();
+ void mainFrameDidChangeToNonInitialEmptyDocument();
PerformanceMonitor* performanceMonitor() { return m_performanceMonitor.get(); }
@@ -1003,6 +1004,7 @@
#endif
Vector<UserContentURLPattern> m_corsDisablingPatterns;
+ Vector<UserStyleSheet> m_userStyleSheetsPendingInjection;
};
inline PageGroup& Page::group()
Modified: trunk/Tools/ChangeLog (257938 => 257939)
--- trunk/Tools/ChangeLog 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Tools/ChangeLog 2020-03-05 20:44:13 UTC (rev 257939)
@@ -1,3 +1,20 @@
+2020-03-05 Antoine Quint <[email protected]>
+
+ Page-specific UserStyleSheets should wait until the initial empty document has been removed to be injected
+ https://bugs.webkit.org/show_bug.cgi?id=208644
+ <rdar://problem/60042429>
+
+ Reviewed by Brady Eidson.
+
+ Add new tests for -[_WKUserStyleSheet initWithSource:forWKWebView:forMainFrameOnly:userContentWorld:]) that check:
+
+ 1. that a _WKUserStyleSheet can be added immediately after a WKWebView's creation and will be injected once the initial
+ empty document has been removed in favor of the document for the requested URL,
+ 2. that removing a _WKUserStyleSheet immediately after it was added but before the initial empty document was removed
+ correctly does not injected the style sheet.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
+
2020-03-05 Aakash Jain <[email protected]>
[ews] Add build step to push commit to WebKit repository
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm (257938 => 257939)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm 2020-03-05 20:32:14 UTC (rev 257938)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm 2020-03-05 20:44:13 UTC (rev 257939)
@@ -526,6 +526,35 @@
expectScriptEvaluatesToColor(otherWebView.get(), backgroundColorScript, redInRGB);
}
+TEST(WKUserContentController, UserStyleSheetAffectingSpecificWebViewInjectionImmediatelyAfterCreation)
+{
+ RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+ [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
+
+ RetainPtr<_WKUserContentWorld> world = [_WKUserContentWorld worldWithName:@"TestWorld"];
+ RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES userContentWorld:world.get()]);
+ [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()];
+
+ [targetWebView _test_waitForDidFinishNavigation];
+
+ expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, greenInRGB);
+}
+
+TEST(WKUserContentController, UserStyleSheetAffectingSpecificWebViewInjectionAndRemovalImmediatelyAfterCreation)
+{
+ RetainPtr<WKWebView> targetWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+ [targetWebView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
+
+ RetainPtr<_WKUserContentWorld> world = [_WKUserContentWorld worldWithName:@"TestWorld"];
+ RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:targetWebView.get() forMainFrameOnly:YES userContentWorld:world.get()]);
+ [[targetWebView configuration].userContentController _addUserStyleSheet:styleSheet.get()];
+ [[targetWebView configuration].userContentController _removeUserStyleSheet:styleSheet.get()];
+
+ [targetWebView _test_waitForDidFinishNavigation];
+
+ expectScriptEvaluatesToColor(targetWebView.get(), backgroundColorScript, redInRGB);
+}
+
TEST(WKUserContentController, UserStyleSheetAffectingOnlySpecificWebViewSharedConfiguration)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);