summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp
diff options
context:
space:
mode:
authorKonstantin Tokarev <annulen@yandex.ru>2016-08-25 19:20:41 +0300
committerKonstantin Tokarev <annulen@yandex.ru>2017-02-02 12:30:55 +0000
commit6882a04fb36642862b11efe514251d32070c3d65 (patch)
treeb7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp
parentab6df191029eeeb0b0f16f127d553265659f739e (diff)
downloadqtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp')
-rw-r--r--Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp425
1 files changed, 425 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp b/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp
new file mode 100644
index 000000000..ae08a2ad5
--- /dev/null
+++ b/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "APIUserContentExtensionStore.h"
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+#include "APIUserContentExtension.h"
+#include "NetworkCacheData.h"
+#include "NetworkCacheDecoder.h"
+#include "NetworkCacheEncoder.h"
+#include "NetworkCacheFileSystem.h"
+#include "SharedMemory.h"
+#include "WebCompiledContentExtension.h"
+#include <WebCore/ContentExtensionCompiler.h>
+#include <WebCore/ContentExtensionError.h>
+#include <string>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RunLoop.h>
+#include <wtf/WorkQueue.h>
+
+using namespace WebKit::NetworkCache;
+
+namespace API {
+
+UserContentExtensionStore& UserContentExtensionStore::defaultStore()
+{
+ static UserContentExtensionStore* defaultStore = adoptRef(new UserContentExtensionStore).leakRef();
+ return *defaultStore;
+}
+
+Ref<UserContentExtensionStore> UserContentExtensionStore::storeWithPath(const WTF::String& storePath)
+{
+ return adoptRef(*new UserContentExtensionStore(storePath));
+}
+
+UserContentExtensionStore::UserContentExtensionStore()
+ : UserContentExtensionStore(defaultStorePath())
+{
+}
+
+UserContentExtensionStore::UserContentExtensionStore(const WTF::String& storePath)
+ : m_storePath(storePath)
+ , m_compileQueue(WorkQueue::create("UserContentExtensionStore Compile Queue", WorkQueue::Type::Concurrent))
+ , m_readQueue(WorkQueue::create("UserContentExtensionStore Read Queue"))
+ , m_removeQueue(WorkQueue::create("UserContentExtensionStore Remove Queue"))
+{
+}
+
+UserContentExtensionStore::~UserContentExtensionStore()
+{
+}
+
+static String constructedPath(const String& base, const String& identifier)
+{
+ return WebCore::pathByAppendingComponent(base, "ContentExtension-" + WebCore::encodeForFileName(identifier));
+}
+
+const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 4 * sizeof(uint64_t);
+struct ContentExtensionMetaData {
+ uint32_t version { UserContentExtensionStore::CurrentContentExtensionFileVersion };
+ uint64_t actionsSize { 0 };
+ uint64_t filtersWithoutDomainsBytecodeSize { 0 };
+ uint64_t filtersWithDomainBytecodeSize { 0 };
+ uint64_t domainFiltersBytecodeSize { 0 };
+
+ size_t fileSize() const
+ {
+ return ContentExtensionFileHeaderSize
+ + actionsSize
+ + filtersWithoutDomainsBytecodeSize
+ + filtersWithDomainBytecodeSize
+ + domainFiltersBytecodeSize;
+ }
+};
+
+static Data encodeContentExtensionMetaData(const ContentExtensionMetaData& metaData)
+{
+ WebKit::NetworkCache::Encoder encoder;
+
+ encoder << metaData.version;
+ encoder << metaData.actionsSize;
+ encoder << metaData.filtersWithoutDomainsBytecodeSize;
+ encoder << metaData.filtersWithDomainBytecodeSize;
+ encoder << metaData.domainFiltersBytecodeSize;
+
+ ASSERT(encoder.bufferSize() == ContentExtensionFileHeaderSize);
+ return Data(encoder.buffer(), encoder.bufferSize());
+}
+
+static bool decodeContentExtensionMetaData(ContentExtensionMetaData& metaData, const Data& fileData)
+{
+ bool success = false;
+ fileData.apply([&metaData, &success, &fileData](const uint8_t* data, size_t size) {
+ // The file data should be mapped into one continuous memory segment so the size
+ // passed to the applier should always equal the data size.
+ if (size != fileData.size())
+ return false;
+
+ WebKit::NetworkCache::Decoder decoder(data, size);
+ if (!decoder.decode(metaData.version))
+ return false;
+ if (!decoder.decode(metaData.actionsSize))
+ return false;
+ if (!decoder.decode(metaData.filtersWithoutDomainsBytecodeSize))
+ return false;
+ if (!decoder.decode(metaData.filtersWithDomainBytecodeSize))
+ return false;
+ if (!decoder.decode(metaData.domainFiltersBytecodeSize))
+ return false;
+ success = true;
+ return false;
+ });
+ return success;
+}
+
+static bool openAndMapContentExtension(const String& path, ContentExtensionMetaData& metaData, Data& fileData)
+{
+ fileData = mapFile(WebCore::fileSystemRepresentation(path).data());
+ if (fileData.isNull())
+ return false;
+
+ if (!decodeContentExtensionMetaData(metaData, fileData))
+ return false;
+
+ return true;
+}
+
+static bool writeDataToFile(const Data& fileData, WebCore::PlatformFileHandle fd)
+{
+ bool success = true;
+ fileData.apply([fd, &success](const uint8_t* data, size_t size) {
+ if (WebCore::writeToFile(fd, (const char*)data, size) == -1) {
+ success = false;
+ return false;
+ }
+ return true;
+ });
+
+ return success;
+}
+
+static std::error_code compiledToFile(String&& json, const String& finalFilePath, ContentExtensionMetaData& metaData, Data& mappedData)
+{
+ using namespace WebCore::ContentExtensions;
+
+ class CompilationClient final : public ContentExtensionCompilationClient {
+ public:
+ CompilationClient(WebCore::PlatformFileHandle fileHandle, ContentExtensionMetaData& metaData)
+ : m_fileHandle(fileHandle)
+ , m_metaData(metaData)
+ {
+ ASSERT(!metaData.actionsSize);
+ ASSERT(!metaData.filtersWithoutDomainsBytecodeSize);
+ ASSERT(!metaData.filtersWithDomainBytecodeSize);
+ ASSERT(!metaData.domainFiltersBytecodeSize);
+ }
+
+ virtual void writeFiltersWithoutDomainsBytecode(Vector<DFABytecode>&& bytecode) override
+ {
+ ASSERT(!m_filtersWithDomainBytecodeWritten);
+ ASSERT(!m_domainFiltersBytecodeWritten);
+ m_filtersWithoutDomainsBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
+ }
+
+ virtual void writeFiltersWithDomainsBytecode(Vector<DFABytecode>&& bytecode) override
+ {
+ ASSERT(!m_domainFiltersBytecodeWritten);
+ m_filtersWithDomainBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
+ }
+
+ virtual void writeDomainFiltersBytecode(Vector<DFABytecode>&& bytecode) override
+ {
+ m_domainFiltersBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
+ }
+
+ virtual void writeActions(Vector<SerializedActionByte>&& actions) override
+ {
+ ASSERT(!m_filtersWithoutDomainsBytecodeWritten);
+ ASSERT(!m_filtersWithDomainBytecodeWritten);
+ ASSERT(!m_domainFiltersBytecodeWritten);
+ ASSERT(!m_actionsWritten);
+ m_actionsWritten += actions.size();
+ writeToFile(Data(actions.data(), actions.size()));
+ }
+
+ virtual void finalize() override
+ {
+ m_metaData.actionsSize = m_actionsWritten;
+ m_metaData.filtersWithoutDomainsBytecodeSize = m_filtersWithoutDomainsBytecodeWritten;
+ m_metaData.filtersWithDomainBytecodeSize = m_filtersWithDomainBytecodeWritten;
+ m_metaData.domainFiltersBytecodeSize = m_domainFiltersBytecodeWritten;
+
+ Data header = encodeContentExtensionMetaData(m_metaData);
+ if (!m_fileError && WebCore::seekFile(m_fileHandle, 0ll, WebCore::FileSeekOrigin::SeekFromBeginning) == -1) {
+ WebCore::closeFile(m_fileHandle);
+ m_fileError = true;
+ }
+ writeToFile(header);
+ }
+
+ bool hadErrorWhileWritingToFile() { return m_fileError; }
+
+ private:
+ void writeToFile(const Data& data)
+ {
+ if (!m_fileError && !writeDataToFile(data, m_fileHandle)) {
+ WebCore::closeFile(m_fileHandle);
+ m_fileError = true;
+ }
+ }
+
+ WebCore::PlatformFileHandle m_fileHandle;
+ ContentExtensionMetaData& m_metaData;
+ size_t m_filtersWithoutDomainsBytecodeWritten { 0 };
+ size_t m_filtersWithDomainBytecodeWritten { 0 };
+ size_t m_domainFiltersBytecodeWritten { 0 };
+ size_t m_actionsWritten { 0 };
+ bool m_fileError { false };
+ };
+
+ auto temporaryFileHandle = WebCore::invalidPlatformFileHandle;
+ String temporaryFilePath = WebCore::openTemporaryFile("ContentExtension", temporaryFileHandle);
+ if (temporaryFileHandle == WebCore::invalidPlatformFileHandle)
+ return UserContentExtensionStore::Error::CompileFailed;
+
+ char invalidHeader[ContentExtensionFileHeaderSize];
+ memset(invalidHeader, 0xFF, sizeof(invalidHeader));
+ // This header will be rewritten in CompilationClient::finalize.
+ if (WebCore::writeToFile(temporaryFileHandle, invalidHeader, sizeof(invalidHeader)) == -1) {
+ WebCore::closeFile(temporaryFileHandle);
+ return UserContentExtensionStore::Error::CompileFailed;
+ }
+
+ CompilationClient compilationClient(temporaryFileHandle, metaData);
+
+ if (auto compilerError = compileRuleList(compilationClient, WTFMove(json))) {
+ WebCore::closeFile(temporaryFileHandle);
+ return compilerError;
+ }
+ if (compilationClient.hadErrorWhileWritingToFile()) {
+ WebCore::closeFile(temporaryFileHandle);
+ return UserContentExtensionStore::Error::CompileFailed;
+ }
+
+ mappedData = adoptAndMapFile(temporaryFileHandle, 0, metaData.fileSize());
+ if (mappedData.isNull())
+ return UserContentExtensionStore::Error::CompileFailed;
+
+ if (!WebCore::moveFile(temporaryFilePath, finalFilePath))
+ return UserContentExtensionStore::Error::CompileFailed;
+
+ return { };
+}
+
+static RefPtr<API::UserContentExtension> createExtension(const String& identifier, const ContentExtensionMetaData& metaData, const Data& fileData)
+{
+ auto sharedMemory = WebKit::SharedMemory::create(const_cast<uint8_t*>(fileData.data()), fileData.size(), WebKit::SharedMemory::Protection::ReadOnly);
+ auto compiledContentExtensionData = WebKit::WebCompiledContentExtensionData(
+ WTFMove(sharedMemory),
+ fileData,
+ ContentExtensionFileHeaderSize,
+ metaData.actionsSize,
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize,
+ metaData.filtersWithoutDomainsBytecodeSize,
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize
+ + metaData.filtersWithoutDomainsBytecodeSize,
+ metaData.filtersWithDomainBytecodeSize,
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize
+ + metaData.filtersWithoutDomainsBytecodeSize
+ + metaData.filtersWithDomainBytecodeSize,
+ metaData.domainFiltersBytecodeSize
+ );
+ auto compiledContentExtension = WebKit::WebCompiledContentExtension::create(WTFMove(compiledContentExtensionData));
+ return API::UserContentExtension::create(identifier, WTFMove(compiledContentExtension));
+}
+
+void UserContentExtensionStore::lookupContentExtension(const WTF::String& identifier, std::function<void(RefPtr<API::UserContentExtension>, std::error_code)> completionHandler)
+{
+ RefPtr<UserContentExtensionStore> self(this);
+ StringCapture identifierCapture(identifier);
+ StringCapture pathCapture(m_storePath);
+
+ m_readQueue->dispatch([self, identifierCapture, pathCapture, completionHandler] {
+ auto path = constructedPath(pathCapture.string(), identifierCapture.string());
+
+ ContentExtensionMetaData metaData;
+ Data fileData;
+ if (!openAndMapContentExtension(path, metaData, fileData)) {
+ RunLoop::main().dispatch([self, completionHandler] {
+ completionHandler(nullptr, Error::LookupFailed);
+ });
+ return;
+ }
+
+ if (metaData.version != UserContentExtensionStore::CurrentContentExtensionFileVersion) {
+ RunLoop::main().dispatch([self, completionHandler] {
+ completionHandler(nullptr, Error::VersionMismatch);
+ });
+ return;
+ }
+
+ RunLoop::main().dispatch([self, identifierCapture, fileData, metaData, completionHandler] {
+ RefPtr<API::UserContentExtension> userContentExtension = createExtension(identifierCapture.string(), metaData, fileData);
+ completionHandler(userContentExtension, { });
+ });
+ });
+}
+
+void UserContentExtensionStore::compileContentExtension(const WTF::String& identifier, WTF::String&& json, std::function<void(RefPtr<API::UserContentExtension>, std::error_code)> completionHandler)
+{
+ RefPtr<UserContentExtensionStore> self(this);
+ StringCapture identifierCapture(identifier);
+ StringCapture jsonCapture(WTFMove(json));
+ StringCapture pathCapture(m_storePath);
+
+ m_compileQueue->dispatch([self, identifierCapture, jsonCapture, pathCapture, completionHandler] () mutable {
+ auto path = constructedPath(pathCapture.string(), identifierCapture.string());
+
+ ContentExtensionMetaData metaData;
+ Data fileData;
+ auto error = compiledToFile(jsonCapture.releaseString(), path, metaData, fileData);
+ if (error) {
+ RunLoop::main().dispatch([self, error, completionHandler] {
+ completionHandler(nullptr, error);
+ });
+ return;
+ }
+
+ RunLoop::main().dispatch([self, identifierCapture, fileData, metaData, completionHandler] {
+ RefPtr<API::UserContentExtension> userContentExtension = createExtension(identifierCapture.string(), metaData, fileData);
+ completionHandler(userContentExtension, { });
+ });
+ });
+}
+
+void UserContentExtensionStore::removeContentExtension(const WTF::String& identifier, std::function<void(std::error_code)> completionHandler)
+{
+ RefPtr<UserContentExtensionStore> self(this);
+ StringCapture identifierCapture(identifier);
+ StringCapture pathCapture(m_storePath);
+
+ m_removeQueue->dispatch([self, identifierCapture, pathCapture, completionHandler] {
+ auto path = constructedPath(pathCapture.string(), identifierCapture.string());
+
+ if (!WebCore::deleteFile(path)) {
+ RunLoop::main().dispatch([self, completionHandler] {
+ completionHandler(Error::RemoveFailed);
+ });
+ return;
+ }
+
+ RunLoop::main().dispatch([self, completionHandler] {
+ completionHandler({ });
+ });
+ });
+}
+
+void UserContentExtensionStore::synchronousRemoveAllContentExtensions()
+{
+ for (const auto& path : WebCore::listDirectory(m_storePath, "*"))
+ WebCore::deleteFile(path);
+}
+
+const std::error_category& userContentExtensionStoreErrorCategory()
+{
+ class UserContentExtensionStoreErrorCategory : public std::error_category {
+ const char* name() const noexcept override
+ {
+ return "user content extension store";
+ }
+
+ virtual std::string message(int errorCode) const override
+ {
+ switch (static_cast<UserContentExtensionStore::Error>(errorCode)) {
+ case UserContentExtensionStore::Error::LookupFailed:
+ return "Unspecified error during lookup.";
+ case UserContentExtensionStore::Error::VersionMismatch:
+ return "Version of file does not match version of interpreter.";
+ case UserContentExtensionStore::Error::CompileFailed:
+ return "Unspecified error during compile.";
+ case UserContentExtensionStore::Error::RemoveFailed:
+ return "Unspecified error during remove.";
+ }
+
+ return std::string();
+ }
+ };
+
+ static NeverDestroyed<UserContentExtensionStoreErrorCategory> contentExtensionErrorCategory;
+ return contentExtensionErrorCategory;
+}
+
+} // namespace API
+
+#endif // ENABLE(CONTENT_EXTENSIONS)