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