source: webkit/trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm@ 286777

Last change on this file since 286777 was 286777, checked in by commit-queue@webkit.org, 3 years ago

Unreviewed, reverting r286764.
https://bugs.webkit.org/show_bug.cgi?id=234081

broke the build

Reverted changeset:

"Add ability to inject messages into webpushd"
https://bugs.webkit.org/show_bug.cgi?id=233988
https://commits.webkit.org/r286764

File size: 13.7 KB
Line 
1/*
2 * Copyright (C) 2021 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27
28#import "DeprecatedGlobalValues.h"
29#import "HTTPServer.h"
30#import "PlatformUtilities.h"
31#import "Test.h"
32#import "TestWKWebView.h"
33#import <WebKit/WKWebViewPrivate.h>
34#import <WebKit/WKWebsiteDataStorePrivate.h>
35#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
36
37static String expectedMessage;
38
39@interface PushAPIMessageHandlerWithExpectedMessage : NSObject <WKScriptMessageHandler>
40@end
41
42@implementation PushAPIMessageHandlerWithExpectedMessage
43- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
44{
45 EXPECT_WK_STREQ(message.body, expectedMessage);
46 done = true;
47}
48@end
49
50// FIXME: Update the test to do subscription before pushing message.
51static const char* mainBytes = R"SWRESOURCE(
52<script>
53function log(msg)
54{
55 window.webkit.messageHandlers.sw.postMessage(msg);
56}
57
58const channel = new MessageChannel();
59channel.port1.onmessage = (event) => log(event.data);
60
61navigator.serviceWorker.register('/sw.js').then((registration) => {
62 if (registration.active) {
63 registration.active.postMessage({port: channel.port2}, [channel.port2]);
64 return;
65 }
66 worker = registration.installing;
67 worker.addEventListener('statechange', function() {
68 if (worker.state == 'activated')
69 worker.postMessage({port: channel.port2}, [channel.port2]);
70 });
71}).catch(function(error) {
72 log("Registration failed with: " + error);
73});
74</script>
75)SWRESOURCE";
76
77static const char* scriptBytes = R"SWRESOURCE(
78let port;
79self.addEventListener("message", (event) => {
80 port = event.data.port;
81 port.postMessage("Ready");
82});
83self.addEventListener("push", (event) => {
84 try {
85 if (!event.data) {
86 port.postMessage("Received: null data");
87 return;
88 }
89 const value = event.data.text();
90 port.postMessage("Received: " + value);
91 if (value != 'Sweet Potatoes')
92 event.waitUntil(Promise.reject('I want sweet potatoes'));
93 } catch (e) {
94 port.postMessage("Got exception " + e);
95 }
96});
97)SWRESOURCE";
98
99static void clearWebsiteDataStore(WKWebsiteDataStore *store)
100{
101 __block bool clearedStore = false;
102 [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
103 clearedStore = true;
104 }];
105 TestWebKitAPI::Util::run(&clearedStore);
106}
107
108static bool pushMessageProcessed = false;
109static bool pushMessageSuccessful = false;
110
111static bool waitUntilEvaluatesToTrue(const Function<bool()>& f)
112{
113 unsigned timeout = 0;
114 do {
115 if (f())
116 return true;
117 TestWebKitAPI::Util::sleep(0.1);
118 } while (++timeout < 100);
119 return false;
120}
121
122TEST(PushAPI, firePushEvent)
123{
124 TestWebKitAPI::HTTPServer server({
125 { "/", { mainBytes } },
126 { "/sw.js", { {{ "Content-Type", "application/javascript" }}, scriptBytes } }
127 }, TestWebKitAPI::HTTPServer::Protocol::Http);
128
129 [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
130
131 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
132
133 auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
134 [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
135
136 clearWebsiteDataStore([configuration websiteDataStore]);
137
138 expectedMessage = "Ready";
139 auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
140 [webView loadRequest:server.request()];
141
142 TestWebKitAPI::Util::run(&done);
143
144 done = false;
145 pushMessageProcessed = false;
146 pushMessageSuccessful = false;
147 NSString *message = @"Sweet Potatoes";
148 expectedMessage = "Received: Sweet Potatoes";
149 [[configuration websiteDataStore] _processPushMessage:[message dataUsingEncoding:NSUTF8StringEncoding] registration:[server.request() URL] completionHandler:^(bool result) {
150 pushMessageSuccessful = result;
151 pushMessageProcessed = true;
152 }];
153 TestWebKitAPI::Util::run(&done);
154
155 TestWebKitAPI::Util::run(&pushMessageProcessed);
156 EXPECT_TRUE(pushMessageSuccessful);
157
158 done = false;
159 pushMessageProcessed = false;
160 pushMessageSuccessful = false;
161 message = @"Rotten Potatoes";
162 expectedMessage = "Received: Rotten Potatoes";
163 [[configuration websiteDataStore] _processPushMessage:[message dataUsingEncoding:NSUTF8StringEncoding] registration:[server.request() URL] completionHandler:^(bool result) {
164 pushMessageSuccessful = result;
165 pushMessageProcessed = true;
166 }];
167 TestWebKitAPI::Util::run(&done);
168
169 TestWebKitAPI::Util::run(&pushMessageProcessed);
170 EXPECT_FALSE(pushMessageSuccessful);
171
172 clearWebsiteDataStore([configuration websiteDataStore]);
173}
174
175static const char* waitOneSecondScriptBytes = R"SWRESOURCE(
176let port;
177self.addEventListener("message", (event) => {
178 port = event.data.port;
179 port.postMessage("Ready");
180});
181self.addEventListener("push", (event) => {
182 if (!event.data)
183 return;
184 const value = event.data.text();
185 if (value === 'Sweet Potatoes')
186 event.waitUntil(new Promise(resolve => setTimeout(resolve, 1000)));
187 else if (value === 'Rotten Potatoes')
188 event.waitUntil(new Promise((resolve, reject) => setTimeout(reject, 1000)));
189 else if (value === 'Timeless Potatoes')
190 event.waitUntil(new Promise(resolve => { }));
191});
192)SWRESOURCE";
193
194static void terminateNetworkProcessWhileRegistrationIsStored(WKWebViewConfiguration *configuration)
195{
196 auto path = configuration.websiteDataStore._configuration._serviceWorkerRegistrationDirectory.path;
197 NSURL* directory = [NSURL fileURLWithPath:path isDirectory:YES];
198 NSURL *swDBPath = [directory URLByAppendingPathComponent:@"ServiceWorkerRegistrations-7.sqlite3"];
199 unsigned timeout = 0;
200 while (![[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path] && ++timeout < 100)
201 TestWebKitAPI::Util::sleep(0.1);
202 // Let's close the SQL database.
203 [configuration.websiteDataStore _sendNetworkProcessWillSuspendImminently];
204 [configuration.websiteDataStore _terminateNetworkProcess];
205}
206
207TEST(PushAPI, firePushEventWithNoPagesSuccessful)
208{
209 TestWebKitAPI::HTTPServer server({
210 { "/", { mainBytes } },
211 { "/sw.js", { {{ "Content-Type", "application/javascript" }}, waitOneSecondScriptBytes } }
212 }, TestWebKitAPI::HTTPServer::Protocol::Http);
213
214 [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
215
216 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
217 clearWebsiteDataStore([configuration websiteDataStore]);
218
219 auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
220 [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
221
222 expectedMessage = "Ready";
223 auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
224 [webView loadRequest:server.request()];
225
226 TestWebKitAPI::Util::run(&done);
227
228 [webView _close];
229 webView = nullptr;
230
231 terminateNetworkProcessWhileRegistrationIsStored(configuration.get());
232
233 // Push event for service worker without any related page, should succeed.
234 pushMessageProcessed = false;
235 pushMessageSuccessful = false;
236 NSString *message = @"Sweet Potatoes";
237 [[configuration websiteDataStore] _processPushMessage:[message dataUsingEncoding:NSUTF8StringEncoding] registration:[server.request() URL] completionHandler:^(bool result) {
238 pushMessageSuccessful = result;
239 pushMessageProcessed = true;
240 }];
241
242 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return [[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
243
244 TestWebKitAPI::Util::run(&pushMessageProcessed);
245 EXPECT_TRUE(pushMessageSuccessful);
246
247 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return ![[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
248
249 clearWebsiteDataStore([configuration websiteDataStore]);
250}
251
252TEST(PushAPI, firePushEventWithNoPagesFail)
253{
254 TestWebKitAPI::HTTPServer server({
255 { "/", { mainBytes } },
256 { "/sw.js", { {{ "Content-Type", "application/javascript" }}, waitOneSecondScriptBytes } }
257 }, TestWebKitAPI::HTTPServer::Protocol::Http);
258
259 [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
260
261 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
262 clearWebsiteDataStore([configuration websiteDataStore]);
263
264 auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
265 [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
266
267 expectedMessage = "Ready";
268 auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
269 [webView loadRequest:server.request()];
270
271 TestWebKitAPI::Util::run(&done);
272
273 [webView _close];
274 webView = nullptr;
275
276 terminateNetworkProcessWhileRegistrationIsStored(configuration.get());
277
278 // Push event for service worker without any related page, should fail.
279 pushMessageProcessed = false;
280 pushMessageSuccessful = false;
281 NSString *message = @"Rotten Potatoes";
282 [[configuration websiteDataStore] _processPushMessage:[message dataUsingEncoding:NSUTF8StringEncoding] registration:[server.request() URL] completionHandler:^(bool result) {
283 pushMessageSuccessful = result;
284 pushMessageProcessed = true;
285 }];
286
287 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return [[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
288
289 TestWebKitAPI::Util::run(&pushMessageProcessed);
290 EXPECT_FALSE(pushMessageSuccessful);
291 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return ![[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
292
293 clearWebsiteDataStore([configuration websiteDataStore]);
294}
295
296TEST(PushAPI, firePushEventWithNoPagesTimeout)
297{
298 TestWebKitAPI::HTTPServer server({
299 { "/", { mainBytes } },
300 { "/sw.js", { {{ "Content-Type", "application/javascript" }}, waitOneSecondScriptBytes } }
301 }, TestWebKitAPI::HTTPServer::Protocol::Http);
302
303 [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
304
305 // Disable service worker delay for the purpose of testing, push event should timeout after 1 second.
306 auto dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
307 auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
308
309 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
310 configuration.get().websiteDataStore = dataStore.get();
311 clearWebsiteDataStore([configuration websiteDataStore]);
312
313 auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
314 [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
315
316 expectedMessage = "Ready";
317 auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
318 [webView loadRequest:server.request()];
319
320 TestWebKitAPI::Util::run(&done);
321
322 [webView _close];
323 webView = nullptr;
324
325 [dataStoreConfiguration setServiceWorkerProcessTerminationDelayEnabled:NO];
326 dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
327 configuration.get().websiteDataStore = dataStore.get();
328
329 terminateNetworkProcessWhileRegistrationIsStored(configuration.get());
330
331 // Push event for service worker without any related page, should timeout so fail.
332 pushMessageProcessed = false;
333 pushMessageSuccessful = false;
334 NSString *message = @"Timeless Potatoes";
335 [[configuration websiteDataStore] _processPushMessage:[message dataUsingEncoding:NSUTF8StringEncoding] registration:[server.request() URL] completionHandler:^(bool result) {
336 pushMessageSuccessful = result;
337 pushMessageProcessed = true;
338 }];
339
340 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return [[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
341
342 TestWebKitAPI::Util::run(&pushMessageProcessed);
343 EXPECT_FALSE(pushMessageSuccessful);
344 EXPECT_TRUE(waitUntilEvaluatesToTrue([&] { return ![[configuration websiteDataStore] _hasServiceWorkerBackgroundActivityForTesting]; }));
345
346 clearWebsiteDataStore([configuration websiteDataStore]);
347}
Note: See TracBrowser for help on using the repository browser.