source: webkit/trunk/Source/WebKit/webpushd/webpushtool/WebPushToolConnection.mm@ 286764

Last change on this file since 286764 was 286764, checked in by beidson@apple.com, 3 years ago

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

Reviewed by Alex Christensen.

Source/WebKit:

Covered by API tests.

This patch:

  • Adds WKWebsiteDataStore SPI to fetch pending push messages for the embedding application
  • Gives webpushd code to inject a push message for a given bundle identifier
  • Gives webpushtool the ability to send one of these fake messages
  • Gives webpushtool the ability to reconnect to the next daemon instance after the current connection is dropped
  • Tests the injection and fetching of push messages via TestWebKitAPI
  • Configurations/webpushtool.xcconfig:
  • WebKit.xcodeproj/project.pbxproj:
  • Resources/webpushtool.entitlements:
  • NetworkProcess/NetworkProcess.cpp:

(WebKit::NetworkProcess::getPendingPushMessages):
(WebKit::NetworkProcess::processPushMessage):

  • NetworkProcess/NetworkProcess.h:
  • NetworkProcess/NetworkProcess.messages.in:
  • NetworkProcess/Notifications/NetworkNotificationManager.cpp:

(WebKit::NetworkNotificationManager::getPendingPushMessages):
(WebKit::ReplyCaller<Vector<WebPushMessage>::callReply):

  • NetworkProcess/Notifications/NetworkNotificationManager.h:
  • Shared/Cocoa/WebPushMessageCocoa.mm: Copied from Source/WebKit/webpushd/webpushtool/WebPushToolConnection.h.

(WebKit::WebPushMessage::fromDictionary):
(WebKit::WebPushMessage::toDictionary const):

  • Shared/PushMessageForTesting.h: Copied from Source/WebKit/Shared/WebPushDaemonConstants.h.

(WebKit::WebPushD::PushMessageForTesting::encode const):
(WebKit::WebPushD::PushMessageForTesting::decode):

  • Shared/WebPushDaemonConstants.h:

(WebKit::WebPushD::messageTypeSendsReply):

  • Shared/WebPushMessage.h: Copied from Source/WebKit/webpushd/webpushtool/WebPushToolConnection.h.

(WebKit::WebPushMessage::encode const):
(WebKit::WebPushMessage::decode):

  • UIProcess/API/Cocoa/WKWebsiteDataStore.mm:

(-[WKWebsiteDataStore _getPendingPushMessages:]):
(-[WKWebsiteDataStore _processPushMessage:completionHandler:]):
(-[WKWebsiteDataStore _processPushMessage:registration:completionHandler:]): Deleted.

  • UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
  • UIProcess/Network/NetworkProcessProxy.cpp:

(WebKit::NetworkProcessProxy::getPendingPushMessages):
(WebKit::NetworkProcessProxy::processPushMessage):

  • UIProcess/Network/NetworkProcessProxy.h:

Tools:

  • TestWebKitAPI/Configurations/TestWebKitAPI-iOS.entitlements:
  • TestWebKitAPI/Configurations/TestWebKitAPI-macOS-internal.entitlements:
  • TestWebKitAPI/Configurations/TestWebKitAPI-macOS.entitlements:
  • TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm:

(messageDictionary):

  • TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm:

(TestWebKitAPI::createMessageDictionary):
(TestWebKitAPI::sendMessageToDaemon):
(TestWebKitAPI::sendMessageToDaemonWaitingForReply):
(TestWebKitAPI::sendConfigurationWithAuditToken):
(TestWebKitAPI::createAndConfigureConnectionToService):
(TestWebKitAPI::encodeString):
(TestWebKitAPI::TEST):
(TestWebKitAPI::function):

  • TestWebKitAPI/cocoa/TestWKWebView.h:
File size: 6.9 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#import "WebPushToolConnection.h"
28
29#import "DaemonEncoder.h"
30#import "DaemonUtilities.h"
31#import "WebPushDaemonConstants.h"
32#import <mach/mach_init.h>
33#import <mach/task.h>
34#import <pal/spi/cocoa/ServersSPI.h>
35#import <wtf/MainThread.h>
36#import <wtf/RetainPtr.h>
37
38namespace WebPushTool {
39
40std::unique_ptr<Connection> Connection::create(Action action, PreferTestService preferTestService, Reconnect reconnect)
41{
42 return makeUnique<Connection>(action, preferTestService, reconnect);
43}
44
45static mach_port_t maybeConnectToService(const char* serviceName)
46{
47 mach_port_t bsPort;
48 task_get_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, &bsPort);
49
50 mach_port_t servicePort;
51 kern_return_t err = bootstrap_look_up(bsPort, serviceName, &servicePort);
52
53 if (err == KERN_SUCCESS)
54 return servicePort;
55
56 return MACH_PORT_NULL;
57}
58
59Connection::Connection(Action action, PreferTestService preferTestService, Reconnect reconnect)
60 : m_action(action)
61 , m_reconnect(reconnect == Reconnect::Yes)
62{
63 if (preferTestService == PreferTestService::Yes)
64 m_serviceName = "org.webkit.webpushtestdaemon.service";
65 else
66 m_serviceName = "com.apple.webkit.webpushd.service";
67}
68
69void Connection::connectToService()
70{
71 if (m_connection)
72 return;
73
74 m_connection = adoptNS(xpc_connection_create_mach_service(m_serviceName, dispatch_get_main_queue(), 0));
75
76 xpc_connection_set_event_handler(m_connection.get(), [this, weakThis = WeakPtr { *this }](xpc_object_t event) {
77 if (!weakThis)
78 return;
79
80 if (event == XPC_ERROR_CONNECTION_INVALID) {
81 printf("Failed to start listening for connections to mach service\n");
82 connectionDropped();
83 return;
84 }
85
86 if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
87 printf("Connection closed\n");
88 if (m_reconnect)
89 printf("===============\nReconnecting...\n");
90 connectionDropped();
91 return;
92 }
93
94 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
95 messageReceived(event);
96 return;
97 }
98
99 RELEASE_ASSERT_NOT_REACHED();
100 });
101
102 auto result = maybeConnectToService(m_serviceName);
103 if (result == MACH_PORT_NULL)
104 printf("Waiting for service '%s' to be available\n", m_serviceName);
105
106 while (result == MACH_PORT_NULL) {
107 usleep(1000);
108 result = maybeConnectToService(m_serviceName);
109 }
110
111 xpc_connection_activate(m_connection.get());
112
113 sendAuditToken();
114 startAction();
115}
116
117void Connection::startAction()
118{
119 switch (m_action) {
120 case Action::StreamDebugMessages:
121 startDebugStreamAction();
122 break;
123 };
124
125 if (m_pushMessage)
126 sendPushMessage();
127}
128
129void Connection::sendPushMessage()
130{
131 ASSERT(m_pushMessage);
132
133 WebKit::Daemon::Encoder encoder;
134 encoder << *m_pushMessage;
135
136 auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
137 xpc_dictionary_set_uint64(dictionary.get(), WebKit::WebPushD::protocolVersionKey, WebKit::WebPushD::protocolVersionValue);
138 xpc_dictionary_set_value(dictionary.get(), WebKit::WebPushD::protocolEncodedMessageKey, WebKit::vectorToXPCData(encoder.takeBuffer()).get());
139 xpc_dictionary_set_uint64(dictionary.get(), WebKit::WebPushD::protocolMessageTypeKey, static_cast<uint64_t>(WebKit::WebPushD::MessageType::InjectPushMessageForTesting));
140
141 xpc_connection_send_message_with_reply(m_connection.get(), dictionary.get(), dispatch_get_main_queue(), ^(xpc_object_t resultMessage) {
142 // This reply handler intentionally left blank
143 });
144}
145
146void Connection::startDebugStreamAction()
147{
148 auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
149 std::array<uint8_t, 1> encodedMessage { 1 };
150 xpc_dictionary_set_uint64(dictionary.get(), "protocol version", 1);
151 xpc_dictionary_set_uint64(dictionary.get(), "message type", 5);
152 xpc_dictionary_set_data(dictionary.get(), "encoded message", encodedMessage.data(), encodedMessage.size());
153
154 xpc_connection_send_message(m_connection.get(), dictionary.get());
155
156 printf("Now streaming debug messages\n");
157}
158
159void Connection::sendAuditToken()
160{
161 audit_token_t token = { 0, 0, 0, 0, 0, 0, 0, 0 };
162 mach_msg_type_number_t auditTokenCount = TASK_AUDIT_TOKEN_COUNT;
163 kern_return_t result = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)(&token), &auditTokenCount);
164 if (result != KERN_SUCCESS) {
165 printf("Unable to get audit token to send\n");
166 return;
167 }
168
169 std::array<uint8_t, 42> encodedMessage;
170 encodedMessage.fill(0);
171 encodedMessage[1] = 1;
172 encodedMessage[2] = 32;
173 memcpy(&encodedMessage[10], &token, sizeof(token));
174 auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
175 xpc_dictionary_set_uint64(dictionary.get(), "protocol version", 1);
176 xpc_dictionary_set_uint64(dictionary.get(), "message type", 6);
177 xpc_dictionary_set_data(dictionary.get(), "encoded message", encodedMessage.data(), encodedMessage.size());
178 xpc_connection_send_message(m_connection.get(), dictionary.get());
179}
180
181void Connection::connectionDropped()
182{
183 m_connection = nullptr;
184 if (m_reconnect) {
185 callOnMainRunLoop([this, weakThis = WeakPtr { this }] {
186 if (weakThis)
187 connectToService();
188 });
189 return;
190 }
191
192 CFRunLoopStop(CFRunLoopGetCurrent());
193}
194
195void Connection::messageReceived(xpc_object_t message)
196{
197 const char* debugMessage = xpc_dictionary_get_string(message, "debug message");
198 if (!debugMessage)
199 return;
200
201 printf("%s\n", debugMessage);
202}
203
204} // namespace WebPushTool
Note: See TracBrowser for help on using the repository browser.