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