1/*
2 * Copyright (C) 2015 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 "APIContentRuleListStore.h"
28
29#if ENABLE(CONTENT_EXTENSIONS)
30
31#include "APIContentRuleList.h"
32#include "NetworkCacheData.h"
33#include "NetworkCacheFileSystem.h"
34#include "SharedMemory.h"
35#include "WebCompiledContentRuleList.h"
36#include <WebCore/ContentExtensionCompiler.h>
37#include <WebCore/ContentExtensionError.h>
38#include <WebCore/ContentExtensionParser.h>
39#include <WebCore/QualifiedName.h>
40#include <WebCore/SharedBuffer.h>
41#include <string>
42#include <wtf/CompletionHandler.h>
43#include <wtf/FileSystem.h>
44#include <wtf/NeverDestroyed.h>
45#include <wtf/RunLoop.h>
46#include <wtf/WorkQueue.h>
47#include <wtf/persistence/PersistentDecoder.h>
48#include <wtf/persistence/PersistentEncoder.h>
49
50
51namespace API {
52using namespace WebKit::NetworkCache;
53using namespace FileSystem;
54
55ContentRuleListStore& ContentRuleListStore::legacyDefaultStore()
56{
57 const bool legacyFilename = true;
58 static ContentRuleListStore* defaultStore = adoptRef(new ContentRuleListStore(legacyFilename)).leakRef();
59 return *defaultStore;
60}
61
62ContentRuleListStore& ContentRuleListStore::nonLegacyDefaultStore()
63{
64 const bool legacyFilename = false;
65 static ContentRuleListStore* defaultStore = adoptRef(new ContentRuleListStore(legacyFilename)).leakRef();
66 return *defaultStore;
67}
68
69ContentRuleListStore& ContentRuleListStore::defaultStore(bool legacyFilename)
70{
71 if (legacyFilename)
72 return legacyDefaultStore();
73 return nonLegacyDefaultStore();
74}
75
76Ref<ContentRuleListStore> ContentRuleListStore::storeWithPath(const WTF::String& storePath, bool legacyFilename)
77{
78 return adoptRef(*new ContentRuleListStore(storePath, legacyFilename));
79}
80
81ContentRuleListStore::ContentRuleListStore(bool legacyFilename)
82 : ContentRuleListStore(defaultStorePath(legacyFilename), legacyFilename)
83{
84}
85
86ContentRuleListStore::ContentRuleListStore(const WTF::String& storePath, bool legacyFilename)
87 : m_storePath(storePath)
88 , m_compileQueue(WorkQueue::create("ContentRuleListStore Compile Queue", WorkQueue::Type::Concurrent))
89 , m_readQueue(WorkQueue::create("ContentRuleListStore Read Queue"))
90 , m_removeQueue(WorkQueue::create("ContentRuleListStore Remove Queue"))
91 , m_legacyFilename(legacyFilename)
92{
93 makeAllDirectories(storePath);
94}
95
96ContentRuleListStore::~ContentRuleListStore()
97{
98}
99
100static const WTF::String& constructedPathPrefix(bool legacyFilename)
101{
102 static NeverDestroyed<WTF::String> prefix("ContentRuleList-");
103 static NeverDestroyed<WTF::String> legacyPrefix("ContentExtension-");
104 if (legacyFilename)
105 return legacyPrefix;
106 return prefix;
107}
108
109static const WTF::String constructedPathFilter(bool legacyFilename)
110{
111 return makeString(constructedPathPrefix(legacyFilename), '*');
112}
113
114static WTF::String constructedPath(const WTF::String& base, const WTF::String& identifier, bool legacyFilename)
115{
116 return pathByAppendingComponent(base, makeString(constructedPathPrefix(legacyFilename), encodeForFileName(identifier)));
117}
118
119// The size and offset of the densely packed bytes in the file, not sizeof and offsetof, which would
120// represent the size and offset of the structure in memory, possibly with compiler-added padding.
121const size_t ContentRuleListFileHeaderSize = 2 * sizeof(uint32_t) + 5 * sizeof(uint64_t);
122const size_t ConditionsApplyOnlyToDomainOffset = sizeof(uint32_t) + 5 * sizeof(uint64_t);
123
124struct ContentRuleListMetaData {
125 uint32_t version { ContentRuleListStore::CurrentContentRuleListFileVersion };
126 uint64_t sourceSize { 0 };
127 uint64_t actionsSize { 0 };
128 uint64_t filtersWithoutConditionsBytecodeSize { 0 };
129 uint64_t filtersWithConditionsBytecodeSize { 0 };
130 uint64_t conditionedFiltersBytecodeSize { 0 };
131 uint32_t conditionsApplyOnlyToDomain { false };
132
133 size_t fileSize() const
134 {
135 return ContentRuleListFileHeaderSize
136 + sourceSize
137 + actionsSize
138 + filtersWithoutConditionsBytecodeSize
139 + filtersWithConditionsBytecodeSize
140 + conditionedFiltersBytecodeSize;
141 }
142};
143
144static WebKit::NetworkCache::Data encodeContentRuleListMetaData(const ContentRuleListMetaData& metaData)
145{
146 WTF::Persistence::Encoder encoder;
147
148 encoder << metaData.version;
149 encoder << metaData.sourceSize;
150 encoder << metaData.actionsSize;
151 encoder << metaData.filtersWithoutConditionsBytecodeSize;
152 encoder << metaData.filtersWithConditionsBytecodeSize;
153 encoder << metaData.conditionedFiltersBytecodeSize;
154 encoder << metaData.conditionsApplyOnlyToDomain;
155
156 ASSERT(encoder.bufferSize() == ContentRuleListFileHeaderSize);
157 return WebKit::NetworkCache::Data(encoder.buffer(), encoder.bufferSize());
158}
159
160template<typename T> void getData(const T&, const Function<bool(const uint8_t*, size_t)>&);
161template<> void getData(const WebKit::NetworkCache::Data& data, const Function<bool(const uint8_t*, size_t)>& function)
162{
163 data.apply(function);
164}
165template<> void getData(const WebCore::SharedBuffer& data, const Function<bool(const uint8_t*, size_t)>& function)
166{
167 function(reinterpret_cast<const uint8_t*>(data.data()), data.size());
168}
169
170template<typename T>
171static Optional<ContentRuleListMetaData> decodeContentRuleListMetaData(const T& fileData)
172{
173 bool success = false;
174 ContentRuleListMetaData metaData;
175 getData(fileData, [&metaData, &success, &fileData](const uint8_t* data, size_t size) {
176 // The file data should be mapped into one continuous memory segment so the size
177 // passed to the applier should always equal the data size.
178 if (size != fileData.size())
179 return false;
180
181 WTF::Persistence::Decoder decoder(data, size);
182 if (!decoder.decode(metaData.version))
183 return false;
184 if (!decoder.decode(metaData.sourceSize))
185 return false;
186 if (!decoder.decode(metaData.actionsSize))
187 return false;
188 if (!decoder.decode(metaData.filtersWithoutConditionsBytecodeSize))
189 return false;
190 if (!decoder.decode(metaData.filtersWithConditionsBytecodeSize))
191 return false;
192 if (!decoder.decode(metaData.conditionedFiltersBytecodeSize))
193 return false;
194 if (!decoder.decode(metaData.conditionsApplyOnlyToDomain))
195 return false;
196 success = true;
197 return false;
198 });
199 if (!success)
200 return WTF::nullopt;
201 return metaData;
202}
203
204struct MappedData {
205 ContentRuleListMetaData metaData;
206 WebKit::NetworkCache::Data data;
207};
208
209static Optional<MappedData> openAndMapOrCopyContentRuleList(const WTF::String& path)
210{
211 FileSystem::makeSafeToUseMemoryMapForPath(path);
212 WebKit::NetworkCache::Data fileData = mapFile(fileSystemRepresentation(path).data());
213 if (fileData.isNull())
214 return WTF::nullopt;
215 auto metaData = decodeContentRuleListMetaData(fileData);
216 if (!metaData)
217 return WTF::nullopt;
218 return {{ WTFMove(*metaData), { WTFMove(fileData) }}};
219}
220
221static bool writeDataToFile(const WebKit::NetworkCache::Data& fileData, PlatformFileHandle fd)
222{
223 bool success = true;
224 fileData.apply([fd, &success](const uint8_t* data, size_t size) {
225 if (writeToFile(fd, (const char*)data, size) == -1) {
226 success = false;
227 return false;
228 }
229 return true;
230 });
231
232 return success;
233}
234
235static Expected<MappedData, std::error_code> compiledToFile(WTF::String&& json, Vector<WebCore::ContentExtensions::ContentExtensionRule>&& parsedRules, const WTF::String& finalFilePath)
236{
237 using namespace WebCore::ContentExtensions;
238
239 class CompilationClient final : public ContentExtensionCompilationClient {
240 public:
241 CompilationClient(PlatformFileHandle fileHandle, ContentRuleListMetaData& metaData)
242 : m_fileHandle(fileHandle)
243 , m_metaData(metaData)
244 {
245 ASSERT(!metaData.sourceSize);
246 ASSERT(!metaData.actionsSize);
247 ASSERT(!metaData.filtersWithoutConditionsBytecodeSize);
248 ASSERT(!metaData.filtersWithConditionsBytecodeSize);
249 ASSERT(!metaData.conditionedFiltersBytecodeSize);
250 ASSERT(!metaData.conditionsApplyOnlyToDomain);
251 }
252
253 void writeSource(WTF::String&& sourceJSON) final
254 {
255 ASSERT(!m_filtersWithoutConditionsBytecodeWritten);
256 ASSERT(!m_filtersWithConditionBytecodeWritten);
257 ASSERT(!m_conditionFiltersBytecodeWritten);
258 ASSERT(!m_actionsWritten);
259 ASSERT(!m_sourceWritten);
260 writeToFile(sourceJSON.is8Bit());
261 m_sourceWritten += sizeof(bool);
262 if (sourceJSON.is8Bit()) {
263 size_t serializedLength = sourceJSON.length() * sizeof(LChar);
264 writeToFile(WebKit::NetworkCache::Data(sourceJSON.characters8(), serializedLength));
265 m_sourceWritten += serializedLength;
266 } else {
267 size_t serializedLength = sourceJSON.length() * sizeof(UChar);
268 writeToFile(WebKit::NetworkCache::Data(reinterpret_cast<const uint8_t*>(sourceJSON.characters16()), serializedLength));
269 m_sourceWritten += serializedLength;
270 }
271 }
272
273 void writeFiltersWithoutConditionsBytecode(Vector<DFABytecode>&& bytecode) final
274 {
275 ASSERT(!m_filtersWithConditionBytecodeWritten);
276 ASSERT(!m_conditionFiltersBytecodeWritten);
277 m_filtersWithoutConditionsBytecodeWritten += bytecode.size();
278 writeToFile(WebKit::NetworkCache::Data(bytecode.data(), bytecode.size()));
279 }
280
281 void writeFiltersWithConditionsBytecode(Vector<DFABytecode>&& bytecode) final
282 {
283 ASSERT(!m_conditionFiltersBytecodeWritten);
284 m_filtersWithConditionBytecodeWritten += bytecode.size();
285 writeToFile(WebKit::NetworkCache::Data(bytecode.data(), bytecode.size()));
286 }
287
288 void writeTopURLFiltersBytecode(Vector<DFABytecode>&& bytecode) final
289 {
290 m_conditionFiltersBytecodeWritten += bytecode.size();
291 writeToFile(WebKit::NetworkCache::Data(bytecode.data(), bytecode.size()));
292 }
293
294 void writeActions(Vector<SerializedActionByte>&& actions, bool conditionsApplyOnlyToDomain) final
295 {
296 ASSERT(!m_filtersWithoutConditionsBytecodeWritten);
297 ASSERT(!m_filtersWithConditionBytecodeWritten);
298 ASSERT(!m_conditionFiltersBytecodeWritten);
299 ASSERT(!m_actionsWritten);
300 m_actionsWritten += actions.size();
301 m_conditionsApplyOnlyToDomain = conditionsApplyOnlyToDomain;
302 writeToFile(WebKit::NetworkCache::Data(actions.data(), actions.size()));
303 }
304
305 void finalize() final
306 {
307 m_metaData.sourceSize = m_sourceWritten;
308 m_metaData.actionsSize = m_actionsWritten;
309 m_metaData.filtersWithoutConditionsBytecodeSize = m_filtersWithoutConditionsBytecodeWritten;
310 m_metaData.filtersWithConditionsBytecodeSize = m_filtersWithConditionBytecodeWritten;
311 m_metaData.conditionedFiltersBytecodeSize = m_conditionFiltersBytecodeWritten;
312 m_metaData.conditionsApplyOnlyToDomain = m_conditionsApplyOnlyToDomain;
313
314 WebKit::NetworkCache::Data header = encodeContentRuleListMetaData(m_metaData);
315 if (!m_fileError && seekFile(m_fileHandle, 0ll, FileSeekOrigin::Beginning) == -1) {
316 closeFile(m_fileHandle);
317 m_fileError = true;
318 }
319 writeToFile(header);
320 }
321
322 bool hadErrorWhileWritingToFile() { return m_fileError; }
323
324 private:
325 void writeToFile(bool value)
326 {
327 writeToFile(WebKit::NetworkCache::Data(reinterpret_cast<const uint8_t*>(&value), sizeof(value)));
328 }
329 void writeToFile(const WebKit::NetworkCache::Data& data)
330 {
331 if (!m_fileError && !writeDataToFile(data, m_fileHandle)) {
332 closeFile(m_fileHandle);
333 m_fileError = true;
334 }
335 }
336
337 PlatformFileHandle m_fileHandle;
338 ContentRuleListMetaData& m_metaData;
339 size_t m_filtersWithoutConditionsBytecodeWritten { 0 };
340 size_t m_filtersWithConditionBytecodeWritten { 0 };
341 size_t m_conditionFiltersBytecodeWritten { 0 };
342 size_t m_actionsWritten { 0 };
343 size_t m_sourceWritten { 0 };
344 bool m_conditionsApplyOnlyToDomain { false };
345 bool m_fileError { false };
346 };
347
348 auto temporaryFileHandle = invalidPlatformFileHandle;
349 WTF::String temporaryFilePath = openTemporaryFile("ContentRuleList", temporaryFileHandle);
350 if (temporaryFileHandle == invalidPlatformFileHandle) {
351 WTFLogAlways("Content Rule List compiling failed: Opening temporary file failed.");
352 return makeUnexpected(ContentRuleListStore::Error::CompileFailed);
353 }
354
355 char invalidHeader[ContentRuleListFileHeaderSize];
356 memset(invalidHeader, 0xFF, sizeof(invalidHeader));
357 // This header will be rewritten in CompilationClient::finalize.
358 if (writeToFile(temporaryFileHandle, invalidHeader, sizeof(invalidHeader)) == -1) {
359 WTFLogAlways("Content Rule List compiling failed: Writing header to file failed.");
360 closeFile(temporaryFileHandle);
361 return makeUnexpected(ContentRuleListStore::Error::CompileFailed);
362 }
363
364 ContentRuleListMetaData metaData;
365 CompilationClient compilationClient(temporaryFileHandle, metaData);
366
367 if (auto compilerError = compileRuleList(compilationClient, WTFMove(json), WTFMove(parsedRules))) {
368 WTFLogAlways("Content Rule List compiling failed: Compiling failed.");
369 closeFile(temporaryFileHandle);
370 return makeUnexpected(compilerError);
371 }
372 if (compilationClient.hadErrorWhileWritingToFile()) {
373 WTFLogAlways("Content Rule List compiling failed: Writing to file failed.");
374 closeFile(temporaryFileHandle);
375 return makeUnexpected(ContentRuleListStore::Error::CompileFailed);
376 }
377
378 auto mappedData = adoptAndMapFile(temporaryFileHandle, 0, metaData.fileSize());
379 if (mappedData.isNull()) {
380 WTFLogAlways("Content Rule List compiling failed: Mapping file failed.");
381 return makeUnexpected(ContentRuleListStore::Error::CompileFailed);
382 }
383
384 if (!moveFile(temporaryFilePath, finalFilePath)) {
385 WTFLogAlways("Content Rule List compiling failed: Moving file failed.");
386 return makeUnexpected(ContentRuleListStore::Error::CompileFailed);
387 }
388
389 FileSystem::makeSafeToUseMemoryMapForPath(finalFilePath);
390
391 return {{ WTFMove(metaData), WTFMove(mappedData) }};
392}
393
394static Ref<API::ContentRuleList> createExtension(const WTF::String& identifier, MappedData&& data)
395{
396 auto sharedMemory = data.data.tryCreateSharedMemory();
397
398 // Content extensions are always compiled to files, and at this point the file
399 // has been already mapped, therefore tryCreateSharedMemory() cannot fail.
400 ASSERT(sharedMemory);
401
402 const size_t headerAndSourceSize = ContentRuleListFileHeaderSize + data.metaData.sourceSize;
403 auto compiledContentRuleListData = WebKit::WebCompiledContentRuleListData(
404 WTFMove(sharedMemory),
405 ConditionsApplyOnlyToDomainOffset,
406 headerAndSourceSize,
407 data.metaData.actionsSize,
408 headerAndSourceSize
409 + data.metaData.actionsSize,
410 data.metaData.filtersWithoutConditionsBytecodeSize,
411 headerAndSourceSize
412 + data.metaData.actionsSize
413 + data.metaData.filtersWithoutConditionsBytecodeSize,
414 data.metaData.filtersWithConditionsBytecodeSize,
415 headerAndSourceSize
416 + data.metaData.actionsSize
417 + data.metaData.filtersWithoutConditionsBytecodeSize
418 + data.metaData.filtersWithConditionsBytecodeSize,
419 data.metaData.conditionedFiltersBytecodeSize
420 );
421 auto compiledContentRuleList = WebKit::WebCompiledContentRuleList::create(WTFMove(compiledContentRuleListData));
422 return API::ContentRuleList::create(identifier, WTFMove(compiledContentRuleList), WTFMove(data.data));
423}
424
425void ContentRuleListStore::lookupContentRuleList(const WTF::String& identifier, CompletionHandler<void(RefPtr<API::ContentRuleList>, std::error_code)> completionHandler)
426{
427 m_readQueue->dispatch([protectedThis = makeRef(*this), identifier = identifier.isolatedCopy(), storePath = m_storePath.isolatedCopy(), legacyFilename = m_legacyFilename, completionHandler = WTFMove(completionHandler)]() mutable {
428 auto path = constructedPath(storePath, identifier, legacyFilename);
429
430 auto contentRuleList = openAndMapOrCopyContentRuleList(path);
431 if (!contentRuleList) {
432 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)] () mutable {
433 completionHandler(nullptr, Error::LookupFailed);
434 });
435 return;
436 }
437
438 if (contentRuleList->metaData.version != ContentRuleListStore::CurrentContentRuleListFileVersion) {
439 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)] () mutable {
440 completionHandler(nullptr, Error::VersionMismatch);
441 });
442 return;
443 }
444
445 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), identifier = identifier.isolatedCopy(), contentRuleList = WTFMove(*contentRuleList), completionHandler = WTFMove(completionHandler)] () mutable {
446 completionHandler(createExtension(identifier, WTFMove(contentRuleList)), { });
447 });
448 });
449}
450
451void ContentRuleListStore::getAvailableContentRuleListIdentifiers(CompletionHandler<void(WTF::Vector<WTF::String>)> completionHandler)
452{
453 m_readQueue->dispatch([protectedThis = makeRef(*this), storePath = m_storePath.isolatedCopy(), legacyFilename = m_legacyFilename, completionHandler = WTFMove(completionHandler)]() mutable {
454
455 Vector<WTF::String> fullPaths = listDirectory(storePath, constructedPathFilter(legacyFilename));
456 Vector<WTF::String> identifiers;
457 identifiers.reserveInitialCapacity(fullPaths.size());
458 const auto prefixLength = constructedPathPrefix(legacyFilename).length();
459 for (const auto& path : fullPaths)
460 identifiers.uncheckedAppend(decodeFromFilename(path.substring(path.reverseFind('/') + 1 + prefixLength)));
461
462 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), identifiers = WTFMove(identifiers)]() mutable {
463 completionHandler(WTFMove(identifiers));
464 });
465 });
466}
467
468void ContentRuleListStore::compileContentRuleList(const WTF::String& identifier, WTF::String&& json, CompletionHandler<void(RefPtr<API::ContentRuleList>, std::error_code)> completionHandler)
469{
470 AtomString::init();
471 WebCore::QualifiedName::init();
472
473 auto parsedRules = WebCore::ContentExtensions::parseRuleList(json);
474 if (!parsedRules.has_value())
475 return completionHandler(nullptr, parsedRules.error());
476
477 m_compileQueue->dispatch([protectedThis = makeRef(*this), identifier = identifier.isolatedCopy(), legacyFilename = m_legacyFilename, json = json.isolatedCopy(), parsedRules = parsedRules.value().isolatedCopy(), storePath = m_storePath.isolatedCopy(), completionHandler = WTFMove(completionHandler)] () mutable {
478 auto path = constructedPath(storePath, identifier, legacyFilename);
479
480 auto result = compiledToFile(WTFMove(json), WTFMove(parsedRules), path);
481 if (!result.has_value()) {
482 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), error = WTFMove(result.error()), completionHandler = WTFMove(completionHandler)] () mutable {
483 completionHandler(nullptr, error);
484 });
485 return;
486 }
487
488 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), identifier = WTFMove(identifier), data = WTFMove(result.value()), completionHandler = WTFMove(completionHandler)] () mutable {
489 auto contentRuleList = createExtension(identifier, WTFMove(data));
490 completionHandler(contentRuleList.ptr(), { });
491 });
492 });
493}
494
495void ContentRuleListStore::removeContentRuleList(const WTF::String& identifier, CompletionHandler<void(std::error_code)> completionHandler)
496{
497 m_removeQueue->dispatch([protectedThis = makeRef(*this), identifier = identifier.isolatedCopy(), storePath = m_storePath.isolatedCopy(), legacyFilename = m_legacyFilename, completionHandler = WTFMove(completionHandler)]() mutable {
498 auto path = constructedPath(storePath, identifier, legacyFilename);
499
500 if (!deleteFile(path)) {
501 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)] () mutable {
502 completionHandler(Error::RemoveFailed);
503 });
504 return;
505 }
506
507 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)] () mutable {
508 completionHandler({ });
509 });
510 });
511}
512
513void ContentRuleListStore::synchronousRemoveAllContentRuleLists()
514{
515 for (const auto& path : listDirectory(m_storePath, "*"))
516 deleteFile(path);
517}
518
519void ContentRuleListStore::invalidateContentRuleListVersion(const WTF::String& identifier)
520{
521 auto file = openFile(constructedPath(m_storePath, identifier, m_legacyFilename), FileOpenMode::Write);
522 if (file == invalidPlatformFileHandle)
523 return;
524 ContentRuleListMetaData invalidHeader = {0, 0, 0, 0, 0, 0};
525 auto bytesWritten = writeToFile(file, reinterpret_cast<const char*>(&invalidHeader), sizeof(invalidHeader));
526 ASSERT_UNUSED(bytesWritten, bytesWritten == sizeof(invalidHeader));
527 closeFile(file);
528}
529
530void ContentRuleListStore::getContentRuleListSource(const WTF::String& identifier, CompletionHandler<void(WTF::String)> completionHandler)
531{
532 m_readQueue->dispatch([protectedThis = makeRef(*this), identifier = identifier.isolatedCopy(), storePath = m_storePath.isolatedCopy(), legacyFilename = m_legacyFilename, completionHandler = WTFMove(completionHandler)]() mutable {
533 auto path = constructedPath(storePath, identifier, legacyFilename);
534
535 auto complete = [protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)](WTF::String source) mutable {
536 RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), source = source.isolatedCopy()] () mutable {
537 completionHandler(source);
538 });
539 };
540
541 auto contentRuleList = openAndMapOrCopyContentRuleList(path);
542 if (!contentRuleList) {
543 complete({ });
544 return;
545 }
546
547 switch (contentRuleList->metaData.version) {
548 case 9:
549 case 10:
550 if (!contentRuleList->metaData.sourceSize) {
551 complete({ });
552 return;
553 }
554 bool is8Bit = contentRuleList->data.data()[ContentRuleListFileHeaderSize];
555 size_t start = ContentRuleListFileHeaderSize + sizeof(bool);
556 size_t length = contentRuleList->metaData.sourceSize - sizeof(bool);
557 if (is8Bit)
558 complete(WTF::String(contentRuleList->data.data() + start, length));
559 else {
560 ASSERT(!(length % sizeof(UChar)));
561 complete(WTF::String(reinterpret_cast<const UChar*>(contentRuleList->data.data() + start), length / sizeof(UChar)));
562 }
563 return;
564 }
565
566 // Older versions cannot recover the original JSON source from disk.
567 complete({ });
568 });
569}
570
571const std::error_category& contentRuleListStoreErrorCategory()
572{
573 class ContentRuleListStoreErrorCategory : public std::error_category {
574 const char* name() const noexcept final
575 {
576 return "content extension store";
577 }
578
579 std::string message(int errorCode) const final
580 {
581 switch (static_cast<ContentRuleListStore::Error>(errorCode)) {
582 case ContentRuleListStore::Error::LookupFailed:
583 return "Unspecified error during lookup.";
584 case ContentRuleListStore::Error::VersionMismatch:
585 return "Version of file does not match version of interpreter.";
586 case ContentRuleListStore::Error::CompileFailed:
587 return "Unspecified error during compile.";
588 case ContentRuleListStore::Error::RemoveFailed:
589 return "Unspecified error during remove.";
590 }
591
592 return std::string();
593 }
594 };
595
596 static NeverDestroyed<ContentRuleListStoreErrorCategory> contentRuleListErrorCategory;
597 return contentRuleListErrorCategory;
598}
599
600} // namespace API
601
602#endif // ENABLE(CONTENT_EXTENSIONS)
603