1/*
2 * Copyright (C) 2014 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#include "config.h"
27#include "WebUserContentController.h"
28
29#include "DataReference.h"
30#include "FrameInfoData.h"
31#include "InjectUserScriptImmediately.h"
32#include "InjectedBundleScriptWorld.h"
33#include "WebCompiledContentRuleList.h"
34#include "WebFrame.h"
35#include "WebPage.h"
36#include "WebProcess.h"
37#include "WebUserContentControllerMessages.h"
38#include "WebUserContentControllerProxyMessages.h"
39#include <WebCore/DOMWrapperWorld.h>
40#include <WebCore/Frame.h>
41#include <WebCore/SecurityOriginData.h>
42#include <WebCore/SerializedScriptValue.h>
43#include <WebCore/UserStyleSheet.h>
44#include <wtf/NeverDestroyed.h>
45
46#if ENABLE(USER_MESSAGE_HANDLERS)
47#include <WebCore/UserMessageHandler.h>
48#include <WebCore/UserMessageHandlerDescriptor.h>
49#endif
50
51namespace WebKit {
52using namespace WebCore;
53
54static HashMap<UserContentControllerIdentifier, WebUserContentController*>& userContentControllers()
55{
56 static NeverDestroyed<HashMap<UserContentControllerIdentifier, WebUserContentController*>> userContentControllers;
57
58 return userContentControllers;
59}
60
61typedef HashMap<uint64_t, std::pair<RefPtr<InjectedBundleScriptWorld>, unsigned>> WorldMap;
62
63static WorldMap& worldMap()
64{
65 static NeverDestroyed<WorldMap> map(std::initializer_list<WorldMap::KeyValuePairType> { { 1, std::make_pair(&InjectedBundleScriptWorld::normalWorld(), 1) } });
66
67 return map;
68}
69
70Ref<WebUserContentController> WebUserContentController::getOrCreate(UserContentControllerIdentifier identifier)
71{
72 auto& userContentControllerPtr = userContentControllers().add(identifier, nullptr).iterator->value;
73 if (userContentControllerPtr)
74 return *userContentControllerPtr;
75
76 RefPtr<WebUserContentController> userContentController = adoptRef(new WebUserContentController(identifier));
77 userContentControllerPtr = userContentController.get();
78
79 return userContentController.releaseNonNull();
80}
81
82WebUserContentController::WebUserContentController(UserContentControllerIdentifier identifier)
83 : m_identifier(identifier)
84{
85 WebProcess::singleton().addMessageReceiver(Messages::WebUserContentController::messageReceiverName(), m_identifier, *this);
86}
87
88WebUserContentController::~WebUserContentController()
89{
90 ASSERT(userContentControllers().contains(m_identifier));
91
92 WebProcess::singleton().removeMessageReceiver(Messages::WebUserContentController::messageReceiverName(), m_identifier);
93
94 userContentControllers().remove(m_identifier);
95}
96
97void WebUserContentController::addUserContentWorlds(const Vector<std::pair<uint64_t, String>>& worlds)
98{
99 for (auto& world : worlds) {
100 ASSERT(world.first);
101 ASSERT(world.first != 1);
102
103 worldMap().ensure(world.first, [&] {
104#if PLATFORM(GTK) || PLATFORM(WPE)
105 // The GLib API doesn't allow to create script worlds from the UI process. We need to
106 // use the existing world created by the web extension if any. The world name is used
107 // as the identifier.
108 if (auto* existingWorld = InjectedBundleScriptWorld::find(world.second))
109 return std::make_pair(Ref<InjectedBundleScriptWorld>(*existingWorld), 1);
110#endif
111 return std::make_pair(InjectedBundleScriptWorld::create(world.second), 1);
112 });
113 }
114}
115
116void WebUserContentController::removeUserContentWorlds(const Vector<uint64_t>& worldIdentifiers)
117{
118 for (auto& worldIdentifier : worldIdentifiers) {
119 ASSERT(worldIdentifier);
120 ASSERT(worldIdentifier != 1);
121
122 auto it = worldMap().find(worldIdentifier);
123 if (it == worldMap().end()) {
124 WTFLogAlways("Trying to remove a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
125 return;
126 }
127
128 it->value.second--;
129
130 if (!it->value.second)
131 worldMap().remove(it);
132 }
133}
134
135void WebUserContentController::addUserScripts(Vector<WebUserScriptData>&& userScripts, InjectUserScriptImmediately immediately)
136{
137 for (const auto& userScriptData : userScripts) {
138 auto it = worldMap().find(userScriptData.worldIdentifier);
139 if (it == worldMap().end()) {
140 WTFLogAlways("Trying to add a UserScript to a UserContentWorld (id=%" PRIu64 ") that does not exist.", userScriptData.worldIdentifier);
141 continue;
142 }
143
144 UserScript script = userScriptData.userScript;
145 addUserScriptInternal(*it->value.first, userScriptData.identifier, WTFMove(script), immediately);
146 }
147}
148
149void WebUserContentController::removeUserScript(uint64_t worldIdentifier, uint64_t userScriptIdentifier)
150{
151 auto it = worldMap().find(worldIdentifier);
152 if (it == worldMap().end()) {
153 WTFLogAlways("Trying to remove a UserScript from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
154 return;
155 }
156
157 removeUserScriptInternal(*it->value.first, userScriptIdentifier);
158}
159
160void WebUserContentController::removeAllUserScripts(const Vector<uint64_t>& worldIdentifiers)
161{
162 for (auto& worldIdentifier : worldIdentifiers) {
163 auto it = worldMap().find(worldIdentifier);
164 if (it == worldMap().end()) {
165 WTFLogAlways("Trying to remove all UserScripts from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
166 return;
167 }
168
169 removeUserScripts(*it->value.first);
170 }
171}
172
173void WebUserContentController::addUserStyleSheets(const Vector<WebUserStyleSheetData>& userStyleSheets)
174{
175 for (const auto& userStyleSheetData : userStyleSheets) {
176 auto it = worldMap().find(userStyleSheetData.worldIdentifier);
177 if (it == worldMap().end()) {
178 WTFLogAlways("Trying to add a UserStyleSheet to a UserContentWorld (id=%" PRIu64 ") that does not exist.", userStyleSheetData.worldIdentifier);
179 continue;
180 }
181
182 UserStyleSheet sheet = userStyleSheetData.userStyleSheet;
183 addUserStyleSheetInternal(*it->value.first, userStyleSheetData.identifier, WTFMove(sheet));
184 }
185
186 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
187}
188
189void WebUserContentController::removeUserStyleSheet(uint64_t worldIdentifier, uint64_t userStyleSheetIdentifier)
190{
191 auto it = worldMap().find(worldIdentifier);
192 if (it == worldMap().end()) {
193 WTFLogAlways("Trying to remove a UserStyleSheet from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
194 return;
195 }
196
197 removeUserStyleSheetInternal(*it->value.first, userStyleSheetIdentifier);
198}
199
200void WebUserContentController::removeAllUserStyleSheets(const Vector<uint64_t>& worldIdentifiers)
201{
202 bool sheetsChanged = false;
203 for (auto& worldIdentifier : worldIdentifiers) {
204 auto it = worldMap().find(worldIdentifier);
205 if (it == worldMap().end()) {
206 WTFLogAlways("Trying to remove all UserStyleSheets from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
207 return;
208 }
209
210 if (m_userStyleSheets.remove(it->value.first.get()))
211 sheetsChanged = true;
212 }
213
214 if (sheetsChanged)
215 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
216}
217
218#if ENABLE(USER_MESSAGE_HANDLERS)
219class WebUserMessageHandlerDescriptorProxy : public WebCore::UserMessageHandlerDescriptor {
220public:
221 static Ref<WebUserMessageHandlerDescriptorProxy> create(WebUserContentController* controller, const String& name, InjectedBundleScriptWorld& world, uint64_t identifier)
222 {
223 return adoptRef(*new WebUserMessageHandlerDescriptorProxy(controller, name, world, identifier));
224 }
225
226 virtual ~WebUserMessageHandlerDescriptorProxy()
227 {
228 }
229
230 uint64_t identifier() { return m_identifier; }
231
232private:
233 WebUserMessageHandlerDescriptorProxy(WebUserContentController* controller, const String& name, InjectedBundleScriptWorld& world, uint64_t identifier)
234 : WebCore::UserMessageHandlerDescriptor(name, world.coreWorld())
235 , m_controller(controller)
236 , m_identifier(identifier)
237 {
238 }
239
240 // WebCore::UserMessageHandlerDescriptor
241 void didPostMessage(WebCore::UserMessageHandler& handler, WebCore::SerializedScriptValue* value) override
242 {
243 WebCore::Frame* frame = handler.frame();
244 if (!frame)
245 return;
246
247 WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
248 if (!webFrame)
249 return;
250
251 WebPage* webPage = webFrame->page();
252 if (!webPage)
253 return;
254
255 WebProcess::singleton().parentProcessConnection()->send(Messages::WebUserContentControllerProxy::DidPostMessage(webPage->pageID(), webFrame->info(), m_identifier, IPC::DataReference(value->data())), m_controller->identifier());
256 }
257
258 RefPtr<WebUserContentController> m_controller;
259 uint64_t m_identifier;
260};
261#endif
262
263void WebUserContentController::addUserScriptMessageHandlers(const Vector<WebScriptMessageHandlerData>& scriptMessageHandlers)
264{
265#if ENABLE(USER_MESSAGE_HANDLERS)
266 for (auto& handler : scriptMessageHandlers) {
267 auto it = worldMap().find(handler.worldIdentifier);
268 if (it == worldMap().end()) {
269 WTFLogAlways("Trying to add a UserScriptMessageHandler to a UserContentWorld (id=%" PRIu64 ") that does not exist.", handler.worldIdentifier);
270 continue;
271 }
272
273 addUserScriptMessageHandlerInternal(*it->value.first, handler.identifier, handler.name);
274 }
275#else
276 UNUSED_PARAM(scriptMessageHandlers);
277#endif
278}
279
280void WebUserContentController::removeUserScriptMessageHandler(uint64_t worldIdentifier, uint64_t userScriptMessageHandlerIdentifier)
281{
282#if ENABLE(USER_MESSAGE_HANDLERS)
283 auto it = worldMap().find(worldIdentifier);
284 if (it == worldMap().end()) {
285 WTFLogAlways("Trying to remove a UserScriptMessageHandler from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
286 return;
287 }
288
289 removeUserScriptMessageHandlerInternal(*it->value.first, userScriptMessageHandlerIdentifier);
290#else
291 UNUSED_PARAM(worldIdentifier);
292 UNUSED_PARAM(userScriptMessageHandlerIdentifier);
293#endif
294}
295
296void WebUserContentController::removeAllUserScriptMessageHandlers(const Vector<uint64_t>& worldIdentifiers)
297{
298#if ENABLE(USER_MESSAGE_HANDLERS)
299 bool userMessageHandlersChanged = false;
300 for (auto& worldIdentifier : worldIdentifiers) {
301 auto it = worldMap().find(worldIdentifier);
302 if (it == worldMap().end()) {
303 WTFLogAlways("Trying to remove all UserScriptMessageHandler from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier);
304 return;
305 }
306
307 if (m_userMessageHandlers.remove(it->value.first.get()))
308 userMessageHandlersChanged = true;
309 }
310
311 if (userMessageHandlersChanged)
312 invalidateAllRegisteredUserMessageHandlerInvalidationClients();
313#else
314 UNUSED_PARAM(worldIdentifiers);
315#endif
316}
317
318#if ENABLE(USER_MESSAGE_HANDLERS)
319void WebUserContentController::addUserScriptMessageHandlerInternal(InjectedBundleScriptWorld& world, uint64_t userScriptMessageHandlerIdentifier, const String& name)
320{
321 auto& messageHandlersInWorld = m_userMessageHandlers.ensure(&world, [] { return Vector<std::pair<uint64_t, RefPtr<WebUserMessageHandlerDescriptorProxy>>> { }; }).iterator->value;
322 if (messageHandlersInWorld.findMatching([&](auto& pair) { return pair.first == userScriptMessageHandlerIdentifier; }) != notFound)
323 return;
324 messageHandlersInWorld.append(std::make_pair(userScriptMessageHandlerIdentifier, WebUserMessageHandlerDescriptorProxy::create(this, name, world, userScriptMessageHandlerIdentifier)));
325}
326
327void WebUserContentController::removeUserScriptMessageHandlerInternal(InjectedBundleScriptWorld& world, uint64_t userScriptMessageHandlerIdentifier)
328{
329 auto it = m_userMessageHandlers.find(&world);
330 if (it == m_userMessageHandlers.end())
331 return;
332
333 auto protectedThis = makeRef(*this);
334
335 auto& userMessageHandlers = it->value;
336 bool userMessageHandlersChanged = userMessageHandlers.removeFirstMatching([userScriptMessageHandlerIdentifier](auto& pair) {
337 return pair.first == userScriptMessageHandlerIdentifier;
338 });
339
340 if (!userMessageHandlersChanged)
341 return;
342
343 if (userMessageHandlers.isEmpty())
344 m_userMessageHandlers.remove(it);
345
346 invalidateAllRegisteredUserMessageHandlerInvalidationClients();
347}
348#endif
349
350#if ENABLE(CONTENT_EXTENSIONS)
351void WebUserContentController::addContentRuleLists(Vector<std::pair<String, WebCompiledContentRuleListData>>&& contentRuleLists)
352{
353 for (auto&& contentRuleList : contentRuleLists) {
354 auto compiledContentRuleList = WebCompiledContentRuleList::create(WTFMove(contentRuleList.second));
355
356 m_contentExtensionBackend.addContentExtension(contentRuleList.first, WTFMove(compiledContentRuleList));
357 }
358}
359
360void WebUserContentController::removeContentRuleList(const String& name)
361{
362 m_contentExtensionBackend.removeContentExtension(name);
363}
364
365void WebUserContentController::removeAllContentRuleLists()
366{
367 m_contentExtensionBackend.removeAllContentExtensions();
368}
369#endif
370
371void WebUserContentController::addUserScriptInternal(InjectedBundleScriptWorld& world, const Optional<uint64_t>& userScriptIdentifier, UserScript&& userScript, InjectUserScriptImmediately immediately)
372{
373 if (immediately == InjectUserScriptImmediately::Yes) {
374 Page::forEachPage([&] (auto& page) {
375 if (&page.userContentProvider() != this)
376 return;
377
378 auto& mainFrame = page.mainFrame();
379 if (userScript.injectedFrames() == InjectInTopFrameOnly) {
380 mainFrame.injectUserScriptImmediately(world.coreWorld(), userScript);
381 return;
382 }
383
384 for (auto* frame = &mainFrame; frame; frame = frame->tree().traverseNext(&mainFrame))
385 frame->injectUserScriptImmediately(world.coreWorld(), userScript);
386 });
387 }
388
389 auto& scriptsInWorld = m_userScripts.ensure(&world, [] { return Vector<std::pair<Optional<uint64_t>, WebCore::UserScript>>(); }).iterator->value;
390 if (userScriptIdentifier && scriptsInWorld.findMatching([&](auto& pair) { return pair.first == userScriptIdentifier; }) != notFound)
391 return;
392
393 scriptsInWorld.append(std::make_pair(userScriptIdentifier, WTFMove(userScript)));
394}
395
396void WebUserContentController::addUserScript(InjectedBundleScriptWorld& world, UserScript&& userScript)
397{
398 addUserScriptInternal(world, WTF::nullopt, WTFMove(userScript), InjectUserScriptImmediately::No);
399}
400
401void WebUserContentController::removeUserScriptWithURL(InjectedBundleScriptWorld& world, const URL& url)
402{
403 auto it = m_userScripts.find(&world);
404 if (it == m_userScripts.end())
405 return;
406
407 auto& scripts = it->value;
408 scripts.removeAllMatching([&](auto& pair) {
409 return pair.second.url() == url;
410 });
411
412 if (scripts.isEmpty())
413 m_userScripts.remove(it);
414}
415
416void WebUserContentController::removeUserScriptInternal(InjectedBundleScriptWorld& world, uint64_t userScriptIdentifier)
417{
418 auto it = m_userScripts.find(&world);
419 if (it == m_userScripts.end())
420 return;
421
422 auto& scripts = it->value;
423 scripts.removeFirstMatching([userScriptIdentifier](auto& pair) {
424 return pair.first == userScriptIdentifier;
425 });
426
427 if (scripts.isEmpty())
428 m_userScripts.remove(it);
429}
430
431void WebUserContentController::removeUserScripts(InjectedBundleScriptWorld& world)
432{
433 m_userScripts.remove(&world);
434}
435
436void WebUserContentController::addUserStyleSheetInternal(InjectedBundleScriptWorld& world, const Optional<uint64_t>& userStyleSheetIdentifier, UserStyleSheet&& userStyleSheet)
437{
438 auto& styleSheetsInWorld = m_userStyleSheets.ensure(&world, [] { return Vector<std::pair<Optional<uint64_t>, WebCore::UserStyleSheet>>(); }).iterator->value;
439 if (userStyleSheetIdentifier && styleSheetsInWorld.findMatching([&](auto& pair) { return pair.first == userStyleSheetIdentifier; }) != notFound)
440 return;
441
442 styleSheetsInWorld.append(std::make_pair(userStyleSheetIdentifier, WTFMove(userStyleSheet)));
443}
444
445void WebUserContentController::addUserStyleSheet(InjectedBundleScriptWorld& world, UserStyleSheet&& userStyleSheet)
446{
447 addUserStyleSheetInternal(world, WTF::nullopt, WTFMove(userStyleSheet));
448 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
449}
450
451void WebUserContentController::removeUserStyleSheetWithURL(InjectedBundleScriptWorld& world, const URL& url)
452{
453 auto it = m_userStyleSheets.find(&world);
454 if (it == m_userStyleSheets.end())
455 return;
456
457 auto& stylesheets = it->value;
458 bool sheetsChanged = stylesheets.removeAllMatching([&](auto& pair) {
459 return pair.second.url() == url;
460 });
461
462 if (!sheetsChanged)
463 return;
464
465 if (stylesheets.isEmpty())
466 m_userStyleSheets.remove(it);
467
468 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
469}
470
471void WebUserContentController::removeUserStyleSheetInternal(InjectedBundleScriptWorld& world, uint64_t userStyleSheetIdentifier)
472{
473 auto it = m_userStyleSheets.find(&world);
474 if (it == m_userStyleSheets.end())
475 return;
476
477 auto& stylesheets = it->value;
478 bool sheetsChanged = stylesheets.removeFirstMatching([userStyleSheetIdentifier](auto& pair) {
479 return pair.first == userStyleSheetIdentifier;
480 });
481
482 if (!sheetsChanged)
483 return;
484
485 if (stylesheets.isEmpty())
486 m_userStyleSheets.remove(it);
487
488 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
489}
490
491void WebUserContentController::removeUserStyleSheets(InjectedBundleScriptWorld& world)
492{
493 if (!m_userStyleSheets.remove(&world))
494 return;
495
496 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
497}
498
499void WebUserContentController::removeAllUserContent()
500{
501 m_userScripts.clear();
502
503 if (!m_userStyleSheets.isEmpty()) {
504 m_userStyleSheets.clear();
505 invalidateInjectedStyleSheetCacheInAllFramesInAllPages();
506 }
507}
508
509void WebUserContentController::forEachUserScript(Function<void(WebCore::DOMWrapperWorld&, const WebCore::UserScript&)>&& functor) const
510{
511 for (const auto& worldAndUserScriptVector : m_userScripts) {
512 auto& world = worldAndUserScriptVector.key->coreWorld();
513 for (const auto& identifierUserScriptPair : worldAndUserScriptVector.value)
514 functor(world, identifierUserScriptPair.second);
515 }
516}
517
518void WebUserContentController::forEachUserStyleSheet(Function<void(const WebCore::UserStyleSheet&)>&& functor) const
519{
520 for (auto& styleSheetVector : m_userStyleSheets.values()) {
521 for (const auto& identifierUserStyleSheetPair : styleSheetVector)
522 functor(identifierUserStyleSheetPair.second);
523 }
524}
525
526#if ENABLE(USER_MESSAGE_HANDLERS)
527void WebUserContentController::forEachUserMessageHandler(Function<void(const WebCore::UserMessageHandlerDescriptor&)>&& functor) const
528{
529 for (auto& userMessageHandlerVector : m_userMessageHandlers.values()) {
530 for (auto& pair : userMessageHandlerVector)
531 functor(*pair.second.get());
532 }
533}
534#endif
535
536} // namespace WebKit
537