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