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