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