Title: [182161] trunk
Revision
182161
Author
[email protected]
Date
2015-03-30 16:22:09 -0700 (Mon, 30 Mar 2015)

Log Message

[Content Extensions] Flesh out the UserContentExtensionStore
https://bugs.webkit.org/show_bug.cgi?id=143123

Patch by Sam Weinig <[email protected]> on 2015-03-30
Reviewed by Benjamin Poulain.

Source/WebCore:

- Adds a variant of getFileSize() that takes a PlatformFileHandle.
- Adds a new file system function, rename() which operates as the POSIX
  rename function does.

* platform/FileSystem.h:
Add new functions.

* platform/posix/FileSystemPOSIX.cpp:
(WebCore::renameFile):
(WebCore::getFileSize):
Add implementations.

* platform/gtk/FileSystemGtk.cpp:
(WebCore::renameFile):
(WebCore::getFileSize):
* platform/win/FileSystemWin.cpp:
(WebCore::getFileSize):
(WebCore::renameFile):
Add stubs.

Source/WebKit2:

* Shared/WebCompiledContentExtension.cpp:
(WebKit::WebCompiledContentExtension::createFromCompiledContentExtensionData):
* Shared/WebCompiledContentExtensionData.cpp:
(WebKit::WebCompiledContentExtensionData::encode):
(WebKit::WebCompiledContentExtensionData::decode):
* Shared/WebCompiledContentExtensionData.h:
(WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
Switch the order in memory of actions and bytecode (and switch the order of the
variables as well, to keep it clear). This will become necessary when streaming
the data to disk, as actions are created before the bytecode and we would have to keep them
in memory until the bytecode was finished compiling if they didn't come before in the file.

* UIProcess/API/APIUserContentExtensionStore.h:
* UIProcess/API/APIUserContentExtensionStore.cpp:
(API::UserContentExtensionStore::defaultStore):
Add accessor for the processes default shared store.

(API::constructedPath):
Helper for constructing the path to a file in the store based on identifier.

(API::encodeContentExtensionMetaData):
(API::decodeContentExtensionMetaData):
Helpers for encoding/decoding the file metadata (version, bytecode size, actions size).

(API::openAndMapContentExtension):
Helper to open and map a filed back content extension.

(API::writeDataToFile):
Helper to write a Data object to a file.

(API::compiledToFile):
Helper to run the content extension compiler and write it to disk. It first
writes it to a temporary file and then does an atomic rename operation to put
the file in the final location. Doing this means that if the process crashes while
the compile is taking place, the partially written file won't end up in the cache,
but rather, will be cleaned up by the OS.

(API::UserContentExtensionStore::lookupContentExtension):
API entry point to lookup an extension that has been compiled to disk. On a background queue,
it attempts to open and map the extension (based on the identifier passed in) and return it
to the caller via the callback function passed in.

(API::UserContentExtensionStore::compileContentExtension):
API entry point to compile an extension and write it to store. On a background queue, it
compiles the file to disk and if successful, returns the memory mapped data via the callback
function passed in.

(API::UserContentExtensionStore::removeContentExtension):
API entry point to remove an extension from the store. On a background queue, it
unlinks the extension (based on the identifier passed in) and tells the caller it completed
the action vial the callback function passed in.

(API::UserContentExtensionStore::synchronousRemoveAllContentExtensions):
Helper for testing which synchronously unlinks all the files in the store.

(API::userContentExtensionStoreErrorCategory):
std::error_code adaptor for some generic errors that can happen in the store. We will want
to flesh these out further.

* UIProcess/API/Cocoa/APIUserContentExtensionStoreCocoa.mm: Added.
(API::UserContentExtensionStore::defaultStorePath):
Helper to get the platform specific path for the store.

* UIProcess/API/C/WKUserContentExtensionStoreRef.cpp:
(WKUserContentExtensionStoreGetTypeID):
Add ENABLE(CONTENT_EXTENSIONS) guards.

* UIProcess/API/Cocoa/_WKUserContentExtensionStore.h:
* UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm:
(-[_WKUserContentExtensionStore dealloc]):
(+[_WKUserContentExtensionStore defaultStore]):
(-[_WKUserContentExtensionStore compileContentExtensionForIdentifier:encodedContentExtension:completionHandler:]):
(-[_WKUserContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
(-[_WKUserContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):
(-[_WKUserContentExtensionStore _removeAllContentExtensions]):
(-[_WKUserContentExtensionStore init]): Deleted.
* UIProcess/API/Cocoa/_WKUserContentExtensionStoreInternal.h:
* UIProcess/API/Cocoa/_WKUserContentExtensionStorePrivate.h: Added.
SPI wrappers for the store.

* WebKit2.xcodeproj/project.pbxproj:
Add new files.

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit2Cocoa/_WKUserContentExtensionStore.mm: Added.
Add tests for _WKUserContentExtensionStore.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (182160 => 182161)


--- trunk/Source/WebCore/ChangeLog	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebCore/ChangeLog	2015-03-30 23:22:09 UTC (rev 182161)
@@ -1,3 +1,30 @@
+2015-03-30  Sam Weinig  <[email protected]>
+
+        [Content Extensions] Flesh out the UserContentExtensionStore
+        https://bugs.webkit.org/show_bug.cgi?id=143123
+
+        Reviewed by Benjamin Poulain.
+
+        - Adds a variant of getFileSize() that takes a PlatformFileHandle.
+        - Adds a new file system function, rename() which operates as the POSIX
+          rename function does.
+
+        * platform/FileSystem.h:
+        Add new functions.
+
+        * platform/posix/FileSystemPOSIX.cpp:
+        (WebCore::renameFile):
+        (WebCore::getFileSize):
+        Add implementations.
+
+        * platform/gtk/FileSystemGtk.cpp:
+        (WebCore::renameFile):
+        (WebCore::getFileSize):
+        * platform/win/FileSystemWin.cpp:
+        (WebCore::getFileSize):
+        (WebCore::renameFile):
+        Add stubs.
+
 2015-03-30  Per Arne Vollan  <[email protected]>
 
         [WinCairo] Crash when closing window while video is loading

Modified: trunk/Source/WebCore/platform/FileSystem.h (182160 => 182161)


--- trunk/Source/WebCore/platform/FileSystem.h	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebCore/platform/FileSystem.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -137,7 +137,9 @@
 WEBCORE_EXPORT bool fileExists(const String&);
 WEBCORE_EXPORT bool deleteFile(const String&);
 WEBCORE_EXPORT bool deleteEmptyDirectory(const String&);
+WEBCORE_EXPORT bool renameFile(const String& oldPath, const String& newPath);
 WEBCORE_EXPORT bool getFileSize(const String&, long long& result);
+WEBCORE_EXPORT bool getFileSize(PlatformFileHandle, long long& result);
 WEBCORE_EXPORT bool getFileModificationTime(const String&, time_t& result);
 WEBCORE_EXPORT bool getFileCreationTime(const String&, time_t& result); // Not all platforms store file creation time.
 bool getFileMetadata(const String&, FileMetadata&);

Modified: trunk/Source/WebCore/platform/gtk/FileSystemGtk.cpp (182160 => 182161)


--- trunk/Source/WebCore/platform/gtk/FileSystemGtk.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebCore/platform/gtk/FileSystemGtk.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -24,6 +24,7 @@
 #include "FileSystem.h"
 
 #include "FileMetadata.h"
+#include "NotImplemented.h"
 #include "UUID.h"
 #include <gio/gio.h>
 #include <glib.h>
@@ -110,6 +111,12 @@
     return filename ? g_rmdir(filename.get()) != -1 : false;
 }
 
+bool renameFile(const String&, const String&)
+{
+    notImplemented();
+    return false;
+}
+
 static bool getFileStat(const String& path, GStatBuf* statBuffer)
 {
     GUniquePtr<gchar> filename = unescapedFilename(path);
@@ -129,6 +136,12 @@
     return true;
 }
 
+bool getFileSize(PlatformFileHandle, long long&)
+{
+    notImplemented();
+    return false;
+}
+
 bool getFileCreationTime(const String&, time_t&)
 {
     // FIXME: Is there a way to retrieve file creation time with Gtk on platforms that support it?

Modified: trunk/Source/WebCore/platform/posix/FileSystemPOSIX.cpp (182160 => 182161)


--- trunk/Source/WebCore/platform/posix/FileSystemPOSIX.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebCore/platform/posix/FileSystemPOSIX.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -71,6 +71,19 @@
     return !unlink(fsRep.data());
 }
 
+bool renameFile(const String& oldPath, const String& newPath)
+{
+    CString oldPathFsRep = fileSystemRepresentation(oldPath);
+    if (!oldPathFsRep.data() || oldPathFsRep.data()[0] == '\0')
+        return false;
+
+    CString newPathFsRep = fileSystemRepresentation(newPath);
+    if (!newPathFsRep.data() || newPathFsRep.data()[0] == '\0')
+        return false;
+
+    return !rename(oldPathFsRep.data(), newPathFsRep.data());
+}
+
 PlatformFileHandle openFile(const String& path, FileOpenMode mode)
 {
     CString fsRep = fileSystemRepresentation(path);
@@ -183,6 +196,16 @@
     return true;
 }
 
+bool getFileSize(PlatformFileHandle handle, long long& result)
+{
+    struct stat fileInfo;
+    if (fstat(handle, &fileInfo))
+        return false;
+
+    result = fileInfo.st_size;
+    return true;
+}
+
 bool getFileCreationTime(const String& path, time_t& result)
 {
 #if OS(DARWIN) || OS(OPENBSD) || OS(NETBSD) || OS(FREEBSD)

Modified: trunk/Source/WebCore/platform/win/FileSystemWin.cpp (182160 => 182161)


--- trunk/Source/WebCore/platform/win/FileSystemWin.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebCore/platform/win/FileSystemWin.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -98,6 +98,12 @@
     return getFileSizeFromFindData(findData, size);
 }
 
+bool getFileSize(PlatformFileHandle, long long&)
+{
+    notImplemented();
+    return false;
+}
+
 bool getFileModificationTime(const String& path, time_t& time)
 {
     WIN32_FIND_DATAW findData;
@@ -154,6 +160,12 @@
     return !!RemoveDirectoryW(filename.charactersWithNullTermination().data());
 }
 
+bool renameFile(const String&, const String&)
+{
+    notImplemented();
+    return false;
+}
+
 String pathByAppendingComponent(const String& path, const String& component)
 {
     Vector<UChar> buffer(MAX_PATH);

Modified: trunk/Source/WebKit2/ChangeLog (182160 => 182161)


--- trunk/Source/WebKit2/ChangeLog	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/ChangeLog	2015-03-30 23:22:09 UTC (rev 182161)
@@ -1,3 +1,93 @@
+2015-03-30  Sam Weinig  <[email protected]>
+
+        [Content Extensions] Flesh out the UserContentExtensionStore
+        https://bugs.webkit.org/show_bug.cgi?id=143123
+
+        Reviewed by Benjamin Poulain.
+
+        * Shared/WebCompiledContentExtension.cpp:
+        (WebKit::WebCompiledContentExtension::createFromCompiledContentExtensionData):
+        * Shared/WebCompiledContentExtensionData.cpp:
+        (WebKit::WebCompiledContentExtensionData::encode):
+        (WebKit::WebCompiledContentExtensionData::decode):
+        * Shared/WebCompiledContentExtensionData.h:
+        (WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
+        Switch the order in memory of actions and bytecode (and switch the order of the
+        variables as well, to keep it clear). This will become necessary when streaming
+        the data to disk, as actions are created before the bytecode and we would have to keep them
+        in memory until the bytecode was finished compiling if they didn't come before in the file.
+
+        * UIProcess/API/APIUserContentExtensionStore.h:
+        * UIProcess/API/APIUserContentExtensionStore.cpp:
+        (API::UserContentExtensionStore::defaultStore):
+        Add accessor for the processes default shared store.
+
+        (API::constructedPath):
+        Helper for constructing the path to a file in the store based on identifier.
+
+        (API::encodeContentExtensionMetaData):
+        (API::decodeContentExtensionMetaData):
+        Helpers for encoding/decoding the file metadata (version, bytecode size, actions size).
+
+        (API::openAndMapContentExtension):
+        Helper to open and map a filed back content extension.
+
+        (API::writeDataToFile):
+        Helper to write a Data object to a file.
+
+        (API::compiledToFile):
+        Helper to run the content extension compiler and write it to disk. It first
+        writes it to a temporary file and then does an atomic rename operation to put
+        the file in the final location. Doing this means that if the process crashes while
+        the compile is taking place, the partially written file won't end up in the cache, 
+        but rather, will be cleaned up by the OS.
+ 
+        (API::UserContentExtensionStore::lookupContentExtension):
+        API entry point to lookup an extension that has been compiled to disk. On a background queue,
+        it attempts to open and map the extension (based on the identifier passed in) and return it
+        to the caller via the callback function passed in.
+        
+        (API::UserContentExtensionStore::compileContentExtension):
+        API entry point to compile an extension and write it to store. On a background queue, it 
+        compiles the file to disk and if successful, returns the memory mapped data via the callback
+        function passed in.
+
+        (API::UserContentExtensionStore::removeContentExtension):
+        API entry point to remove an extension from the store. On a background queue, it 
+        unlinks the extension (based on the identifier passed in) and tells the caller it completed
+        the action vial the callback function passed in.
+
+        (API::UserContentExtensionStore::synchronousRemoveAllContentExtensions):
+        Helper for testing which synchronously unlinks all the files in the store.
+
+        (API::userContentExtensionStoreErrorCategory):
+        std::error_code adaptor for some generic errors that can happen in the store. We will want
+        to flesh these out further.
+
+        * UIProcess/API/Cocoa/APIUserContentExtensionStoreCocoa.mm: Added.
+        (API::UserContentExtensionStore::defaultStorePath):
+        Helper to get the platform specific path for the store.
+
+        * UIProcess/API/C/WKUserContentExtensionStoreRef.cpp:
+        (WKUserContentExtensionStoreGetTypeID):
+        Add ENABLE(CONTENT_EXTENSIONS) guards.
+
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStore.h:
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm:
+        (-[_WKUserContentExtensionStore dealloc]):
+        (+[_WKUserContentExtensionStore defaultStore]):
+        (-[_WKUserContentExtensionStore compileContentExtensionForIdentifier:encodedContentExtension:completionHandler:]):
+        (-[_WKUserContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
+        (-[_WKUserContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):
+        (-[_WKUserContentExtensionStore _removeAllContentExtensions]):
+        (-[_WKUserContentExtensionStore init]): Deleted.
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStoreInternal.h:
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStorePrivate.h: Added.
+        SPI wrappers for the store.
+
+        * WebKit2.xcodeproj/project.pbxproj:
+        Add new files.
+
 2015-03-30  Antti Koivisto  <[email protected]>
 
         Don't cache resources that are very unlikely to be reused

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp (182160 => 182161)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -49,15 +49,15 @@
 Ref<WebCompiledContentExtension> WebCompiledContentExtension::createFromCompiledContentExtensionData(const WebCore::ContentExtensions::CompiledContentExtensionData& compilerData)
 {
     RefPtr<SharedMemory> sharedMemory = SharedMemory::create(compilerData.bytecode.size() + compilerData.actions.size());
-    memcpy(static_cast<char*>(sharedMemory->data()), compilerData.bytecode.data(), compilerData.bytecode.size());
-    memcpy(static_cast<char*>(sharedMemory->data()) + compilerData.bytecode.size(), compilerData.actions.data(), compilerData.actions.size());
+    memcpy(static_cast<char*>(sharedMemory->data()), compilerData.actions.data(), compilerData.actions.size());
+    memcpy(static_cast<char*>(sharedMemory->data()) + compilerData.actions.size(), compilerData.bytecode.data(), compilerData.bytecode.size());
 
     WebCompiledContentExtensionData data;
     data.data = ""
-    data.bytecodeOffset = 0;
+    data.actionsOffset = 0;
+    data.actionsSize = compilerData.actions.size();
+    data.bytecodeOffset = compilerData.actions.size();
     data.bytecodeSize = compilerData.bytecode.size();
-    data.actionsOffset = compilerData.bytecode.size();
-    data.actionsSize = compilerData.actions.size();
 
     return create(WTF::move(data));
 }

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp (182160 => 182161)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -38,10 +38,10 @@
     data->createHandle(handle, SharedMemory::ReadOnly);
     encoder << handle;
 
-    encoder << bytecodeOffset;
-    encoder << bytecodeSize;
     encoder << actionsOffset;
     encoder << actionsSize;
+    encoder << bytecodeOffset;
+    encoder << bytecodeSize;
 }
 
 bool WebCompiledContentExtensionData::decode(IPC::ArgumentDecoder& decoder, WebCompiledContentExtensionData& compiledContentExtensionData)
@@ -51,16 +51,15 @@
         return false;
     compiledContentExtensionData.data = "" SharedMemory::ReadOnly);
 
-    if (!decoder.decode(compiledContentExtensionData.bytecodeOffset))
-        return false;
-    if (!decoder.decode(compiledContentExtensionData.bytecodeSize))
-        return false;
     if (!decoder.decode(compiledContentExtensionData.actionsOffset))
         return false;
     if (!decoder.decode(compiledContentExtensionData.actionsSize))
         return false;
+    if (!decoder.decode(compiledContentExtensionData.bytecodeOffset))
+        return false;
+    if (!decoder.decode(compiledContentExtensionData.bytecodeSize))
+        return false;
 
-
     return true;
 }
 

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h (182160 => 182161)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -44,12 +44,12 @@
     {
     }
     
-    WebCompiledContentExtensionData(RefPtr<SharedMemory> data, unsigned bytecodeOffset, unsigned bytecodeSize, unsigned actionsOffset, unsigned actionsSize)
+    WebCompiledContentExtensionData(RefPtr<SharedMemory> data, unsigned actionsOffset, unsigned actionsSize, unsigned bytecodeOffset, unsigned bytecodeSize)
         : data(data)
+        , actionsOffset(actionsOffset)
+        , actionsSize(actionsSize)
         , bytecodeOffset(bytecodeOffset)
         , bytecodeSize(bytecodeSize)
-        , actionsOffset(actionsOffset)
-        , actionsSize(actionsSize)
     {
     }
 
@@ -57,10 +57,10 @@
     static bool decode(IPC::ArgumentDecoder&, WebCompiledContentExtensionData&);
 
     RefPtr<SharedMemory> data;
+    unsigned actionsOffset { 0 };
+    unsigned actionsSize { 0 };
     unsigned bytecodeOffset { 0 };
     unsigned bytecodeSize { 0 };
-    unsigned actionsOffset { 0 };
-    unsigned actionsSize { 0 };
 };
 
 }

Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -26,14 +26,332 @@
 #include "config.h"
 #include "APIUserContentExtensionStore.h"
 
+#if ENABLE(CONTENT_EXTENSIONS)
+
+#include "APIUserContentExtension.h"
+#include "NetworkCacheData.h"
+#include "NetworkCacheDecoder.h"
+#include "NetworkCacheEncoder.h"
+#include "NetworkCacheFileSystemPosix.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;
+}
+
 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));
+}
+
+struct ContentExtensionMetaData {
+    uint32_t version;
+    uint64_t actionsOffset;
+    uint64_t actionsSize;
+    uint64_t bytecodeOffset;
+    uint64_t bytecodeSize;
+};
+
+static Data encodeContentExtensionMetaData(const ContentExtensionMetaData& metaData)
+{
+    WebKit::NetworkCache::Encoder encoder;
+
+    encoder << metaData.version;
+    encoder << metaData.actionsSize;
+    encoder << metaData.bytecodeSize;
+
+    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.bytecodeSize))
+            return false;
+        metaData.actionsOffset = decoder.currentOffset();
+        metaData.bytecodeOffset = metaData.actionsOffset + metaData.actionsSize;
+        success = true;
+        return false;
+    });
+    return success;
+}
+
+static bool openAndMapContentExtension(const String& path, ContentExtensionMetaData& metaData, Data& fileData)
+{
+    auto fd = WebCore::openFile(path, WebCore::OpenForRead);
+    if (fd == WebCore::invalidPlatformFileHandle)
+        return false;
+
+    long long fileSize = 0;
+    if (!WebCore::getFileSize(fd, fileSize)) {
+        WebCore::closeFile(fd);
+        return false;
+    }
+
+    fileData = mapFile(fd, 0, static_cast<size_t>(fileSize));
+    WebCore::closeFile(fd);
+
+    if (fileData.isNull())
+        return false;
+
+    if (!decodeContentExtensionMetaData(metaData, fileData))
+        return false;
+
+    return true;
+}
+
+static bool writeDataToFile(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(const String& json, const String& finalFilePath, ContentExtensionMetaData& metaData, Data& mappedData)
+{
+    using namespace WebCore::ContentExtensions;
+
+    class CompilationClient final : public ContentExtensionCompilationClient {
+    public:
+        CompilationClient(Data& bytecodeData, Data& actionsData)
+            : m_bytecodeData(bytecodeData)
+            , m_actionsData(actionsData)
+        {
+        }
+
+        virtual void writeBytecode(Vector<DFABytecode>&& bytecode) override
+        {
+            m_bytecodeData = Data(bytecode.data(), bytecode.size());
+        }
+
+        virtual void writeActions(Vector<SerializedActionByte>&& actions) override
+        {
+            m_actionsData = Data(actions.data(), actions.size());
+        }
+
+    private:
+        Data& m_bytecodeData;
+        Data& m_actionsData;
+    };
+
+    Data bytecode;
+    Data actions;
+    CompilationClient compilationClient(bytecode, actions);
+
+    // FIXME: This copies the data. Instead, we should be passing an interface
+    // to the compiler that can write directly to a file.
+
+    auto compilerError = compileRuleList(compilationClient, json);
+    if (compilerError)
+        return compilerError;
+
+    auto actionsAndBytecode = concatenate(actions, bytecode);
+
+    metaData.version = 1;
+    metaData.actionsSize = actions.size();
+    metaData.bytecodeSize = bytecode.size();
+
+    auto encodedMetaData = encodeContentExtensionMetaData(metaData);
+
+    metaData.actionsOffset = encodedMetaData.size();
+    metaData.bytecodeOffset = encodedMetaData.size() + metaData.actionsSize;
+
+    auto data = "" actionsAndBytecode);
+    auto dataSize = data.size();
+
+    ASSERT(metaData.actionsOffset + metaData.actionsSize + metaData.bytecodeSize == dataSize);
+
+    auto temporaryFileHandle = WebCore::invalidPlatformFileHandle;
+    String temporaryFilePath = WebCore::openTemporaryFile("ContentExtension", temporaryFileHandle);
+    if (temporaryFileHandle == WebCore::invalidPlatformFileHandle)
+        return UserContentExtensionStore::Error::CompileFailed;
+
+    if (!writeDataToFile(data, temporaryFileHandle)) {
+        WebCore::closeFile(temporaryFileHandle);
+        return UserContentExtensionStore::Error::CompileFailed;
+    }
+
+    mappedData = mapFile(temporaryFileHandle, 0, dataSize);
+    WebCore::closeFile(temporaryFileHandle);
+
+    if (mappedData.isNull())
+        return UserContentExtensionStore::Error::CompileFailed;
+
+    if (!WebCore::renameFile(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::createFromVMBuffer(const_cast<uint8_t*>(fileData.data()), fileData.size());
+    auto compiledContentExtensionData = WebKit::WebCompiledContentExtensionData(
+        sharedMemory,
+        metaData.actionsOffset,
+        metaData.actionsSize,
+        metaData.bytecodeOffset,
+        metaData.bytecodeSize
+    );
+    auto compiledContentExtension = WebKit::WebCompiledContentExtension::create(WTF::move(compiledContentExtensionData));
+    return API::UserContentExtension::create(identifier, WTF::move(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;
+        }
+        
+        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, const WTF::String& json, std::function<void(RefPtr<API::UserContentExtension>, std::error_code)> completionHandler)
+{
+    RefPtr<UserContentExtensionStore> self(this);
+    StringCapture identifierCapture(identifier);
+    StringCapture jsonCapture(json);
+    StringCapture pathCapture(m_storePath);
+
+    m_compileQueue->dispatch([self, identifierCapture, jsonCapture, pathCapture, completionHandler] {
+        auto path = constructedPath(pathCapture.string(), identifierCapture.string());
+
+        ContentExtensionMetaData metaData;
+        Data fileData;
+        auto error = compiledToFile(jsonCapture.string(), 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::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)

Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -26,16 +26,62 @@
 #ifndef APIUserContentExtensionStore_h
 #define APIUserContentExtensionStore_h
 
+#if ENABLE(CONTENT_EXTENSIONS)
+
 #include "APIObject.h"
+#include <system_error>
+#include <wtf/text/WTFString.h>
 
+namespace WTF {
+class WorkQueue;
+}
+
 namespace API {
 
+class UserContentExtension;
+
 class UserContentExtensionStore final : public ObjectImpl<Object::Type::UserContentExtensionStore> {
 public:
+    enum class Error {
+        LookupFailed = 1,
+        CompileFailed,
+        RemoveFailed
+    };
+
+    static UserContentExtensionStore& defaultStore();
+
     explicit UserContentExtensionStore();
+    explicit UserContentExtensionStore(const WTF::String& storePath);
     virtual ~UserContentExtensionStore();
+
+    void compileContentExtension(const WTF::String& identifier, const WTF::String& json, std::function<void(RefPtr<API::UserContentExtension>, std::error_code)>);
+    void lookupContentExtension(const WTF::String& identifier, std::function<void(RefPtr<API::UserContentExtension>, std::error_code)>);
+    void removeContentExtension(const WTF::String& identifier, std::function<void(std::error_code)>);
+
+    // For testing only.
+    void synchronousRemoveAllContentExtensions();
+
+private:
+    WTF::String defaultStorePath();
+
+    const WTF::String m_storePath;
+    Ref<WTF::WorkQueue> m_compileQueue;
+    Ref<WTF::WorkQueue> m_readQueue;
+    Ref<WTF::WorkQueue> m_removeQueue;
 };
 
+const std::error_category& userContentExtensionStoreErrorCategory();
+
+inline std::error_code make_error_code(UserContentExtensionStore::Error error)
+{
+    return { static_cast<int>(error), userContentExtensionStoreErrorCategory() };
+}
+
 } // namespace API
 
+namespace std {
+    template<> struct is_error_code_enum<API::UserContentExtensionStore::Error> : public true_type { };
+}
+
+#endif // ENABLE(CONTENT_EXTENSIONS)
 #endif // APIUserContentExtensionStore_h

Modified: trunk/Source/WebKit2/UIProcess/API/C/WKUserContentExtensionStoreRef.cpp (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/C/WKUserContentExtensionStoreRef.cpp	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/C/WKUserContentExtensionStoreRef.cpp	2015-03-30 23:22:09 UTC (rev 182161)
@@ -33,5 +33,9 @@
 
 WKTypeID WKUserContentExtensionStoreGetTypeID()
 {
+#if ENABLE(CONTENT_EXTENSIONS)
     return toAPI(API::UserContentExtensionStore::APIType);
+#else
+    return 0;
+#endif
 }

Copied: trunk/Source/WebKit2/UIProcess/API/Cocoa/APIUserContentExtensionStoreCocoa.mm (from rev 182160, trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp) (0 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/APIUserContentExtensionStoreCocoa.mm	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/APIUserContentExtensionStoreCocoa.mm	2015-03-30 23:22:09 UTC (rev 182161)
@@ -0,0 +1,65 @@
+/*
+ * 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 "SandboxUtilities.h"
+
+namespace API {
+
+String UserContentExtensionStore::defaultStorePath()
+{
+    static dispatch_once_t onceToken;
+    static NSURL *contentExtensionStoreURL;
+
+    dispatch_once(&onceToken, ^{
+        NSURL *url = "" defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nullptr create:NO error:nullptr];
+        if (!url)
+            RELEASE_ASSERT_NOT_REACHED();
+
+        url = "" URLByAppendingPathComponent:@"WebKit" isDirectory:YES];
+
+        if (!WebKit::processHasContainer()) {
+            NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
+            if (!bundleIdentifier)
+                bundleIdentifier = [NSProcessInfo processInfo].processName;
+            url = "" URLByAppendingPathComponent:bundleIdentifier isDirectory:YES];
+        }
+
+        contentExtensionStoreURL = [[url URLByAppendingPathComponent:@"ContentExtensions" isDirectory:YES] retain];
+    });
+
+    if (![[NSFileManager defaultManager] createDirectoryAtURL:contentExtensionStoreURL withIntermediateDirectories:YES attributes:nil error:nullptr])
+        LOG_ERROR("Failed to create directory %@", contentExtensionStoreURL);
+
+    return contentExtensionStoreURL.absoluteURL.path.fileSystemRepresentation;
+}
+
+} // namespace API
+
+#endif // ENABLE(CONTENT_EXTENSIONS)

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -27,9 +27,17 @@
 
 #if WK_API_ENABLED
 
+@class _WKUserContentFilter;
+
 WK_CLASS_AVAILABLE(WK_MAC_TBA, WK_IOS_TBA)
 @interface _WKUserContentExtensionStore : NSObject
 
++ (instancetype)defaultStore;
+
+- (void)compileContentExtensionForIdentifier:(NSString *)identifier encodedContentExtension:(NSString *)encodedContentExtension completionHandler:(void (^)(_WKUserContentFilter *, NSError *))completionHandler;
+- (void)lookupContentExtensionForIdentifier:(NSString *)identifier completionHandler:(void (^)(_WKUserContentFilter *, NSError *))completionHandler;
+- (void)removeContentExtensionForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSError *))completionHandler;
+
 @end
 
 #endif // WK_API_ENABLED

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm	2015-03-30 23:22:09 UTC (rev 182161)
@@ -28,25 +28,80 @@
 
 #if WK_API_ENABLED
 
+#import "WKErrorInternal.h"
+#import "_WKUserContentFilterInternal.h"
+
 @implementation _WKUserContentExtensionStore
 
-- (instancetype)init
+- (void)dealloc
 {
-    if (!(self = [super init]))
-        return nil;
+    _userContentExtensionStore->~UserContentExtensionStore();
 
-    API::Object::constructInWrapper<API::UserContentExtensionStore>(self);
+    [super dealloc];
+}
 
-    return self;
++ (instancetype)defaultStore
+{
+    return WebKit::wrapper(API::UserContentExtensionStore::defaultStore());
 }
 
-- (void)dealloc
+- (void)compileContentExtensionForIdentifier:(NSString *)identifier encodedContentExtension:(NSString *)encodedContentExtension completionHandler:(void (^)(_WKUserContentFilter *, NSError *))completionHandler
 {
-    _userContentExtensionStore->~UserContentExtensionStore();
+    auto handler = adoptNS([completionHandler copy]);
 
-    [super dealloc];
+    _userContentExtensionStore->compileContentExtension(identifier, encodedContentExtension, [handler](RefPtr<API::UserContentExtension> contentExtension, std::error_code error) {
+        if (error) {
+            auto rawHandler = (void (^)(_WKUserContentFilter *, NSError *))handler.get();
+            
+            // FIXME: Pass real error.
+            auto error = createNSError(WKErrorUnknown);
+            rawHandler(nil, error.get());
+            return;
+        }
+
+        auto rawHandler = (void (^)(_WKUserContentFilter *, NSError *))handler.get();
+        rawHandler(WebKit::wrapper(*contentExtension.get()), nil);
+    });
 }
 
+- (void)lookupContentExtensionForIdentifier:(NSString *)identifier completionHandler:(void (^)(_WKUserContentFilter *, NSError *))completionHandler
+{
+    auto handler = adoptNS([completionHandler copy]);
+
+    _userContentExtensionStore->lookupContentExtension(identifier, [handler](RefPtr<API::UserContentExtension> contentExtension, std::error_code error) {
+        if (error) {
+            auto rawHandler = (void (^)(_WKUserContentFilter *, NSError *))handler.get();
+
+            // FIXME: Pass real error.
+            auto error = createNSError(WKErrorUnknown);
+            rawHandler(nil, error.get());
+            return;
+        }
+
+        auto rawHandler = (void (^)(_WKUserContentFilter *, NSError *))handler.get();
+        rawHandler(WebKit::wrapper(*contentExtension.get()), nil);
+    });
+}
+
+- (void)removeContentExtensionForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSError *))completionHandler
+{
+    auto handler = adoptNS([completionHandler copy]);
+
+    _userContentExtensionStore->removeContentExtension(identifier, [handler](std::error_code error) {
+        if (error) {
+            auto rawHandler = (void (^)(NSError *))handler.get();
+
+            // FIXME: Pass real error.
+            auto error = createNSError(WKErrorUnknown);
+            rawHandler(error.get());
+            return;
+        }
+
+        auto rawHandler = (void (^)(NSError *))handler.get();
+        rawHandler(nil);
+    });
+}
+
 #pragma mark WKObject protocol implementation
 
 - (API::Object&)_apiObject
@@ -56,4 +111,15 @@
 
 @end
 
+@implementation _WKUserContentExtensionStore (WKPrivate)
+
+// For testing only.
+
+- (void)_removeAllContentExtensions
+{
+    _userContentExtensionStore->synchronousRemoveAllContentExtensions();
+}
+
+@end
+
 #endif // WK_API_ENABLED

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStoreInternal.h (182160 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStoreInternal.h	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStoreInternal.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -23,7 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import "_WKUserContentExtensionStore.h"
+#import "_WKUserContentExtensionStorePrivate.h"
 
 #if WK_API_ENABLED
 

Copied: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStorePrivate.h (from rev 182160, trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h) (0 => 182161)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStorePrivate.h	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStorePrivate.h	2015-03-30 23:22:09 UTC (rev 182161)
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#import <WebKit/_WKUserContentExtensionStore.h>
+
+#if WK_API_ENABLED
+
+@interface _WKUserContentExtensionStore (WKPrivate)
+
+// For testing only.
+- (void)_removeAllContentExtensions;
+
+@end
+
+#endif

Modified: trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj (182160 => 182161)


--- trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj	2015-03-30 23:22:09 UTC (rev 182161)
@@ -1138,6 +1138,7 @@
 		7C8EB11718DB6A19007917C2 /* WKPreferencesPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C8EB11618DB6A19007917C2 /* WKPreferencesPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7C9D1537184584DA009D3918 /* WKBrowsingContextGroupInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C9D1536184584DA009D3918 /* WKBrowsingContextGroupInternal.h */; };
 		7CA254EB182993CE00FC8A41 /* WKBrowsingContextPolicyDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CA254EA182993CE00FC8A41 /* WKBrowsingContextPolicyDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		7CA3793E1AC378B30079DC37 /* _WKUserContentExtensionStorePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CA3793D1AC378B30079DC37 /* _WKUserContentExtensionStorePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7CB16FEF1724BA23007A0A95 /* com.apple.ist.ds.appleconnect.webplugin.sb in Copy Plug-in Sandbox Profiles */ = {isa = PBXBuildFile; fileRef = 7CB16FE21724B9B5007A0A95 /* com.apple.ist.ds.appleconnect.webplugin.sb */; };
 		7CB16FF01724BA24007A0A95 /* com.apple.QuickTime Plugin.plugin.sb in Copy Plug-in Sandbox Profiles */ = {isa = PBXBuildFile; fileRef = 7CB16FE31724B9B5007A0A95 /* com.apple.QuickTime Plugin.plugin.sb */; };
 		7CB16FF11724BA26007A0A95 /* com.apple.WebKit.plugin-common.sb in Copy Plug-in Sandbox Profiles */ = {isa = PBXBuildFile; fileRef = 7CB16FE41724B9B5007A0A95 /* com.apple.WebKit.plugin-common.sb */; };
@@ -1169,6 +1170,7 @@
 		7CE4D2201A4914CA00C7F152 /* APIProcessPoolConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CE4D21E1A4914CA00C7F152 /* APIProcessPoolConfiguration.h */; };
 		7CE4D2271A4916C200C7F152 /* WebProcessPoolMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CE4D2251A4916C200C7F152 /* WebProcessPoolMessageReceiver.cpp */; };
 		7CE4D2281A4916C200C7F152 /* WebProcessPoolMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CE4D2261A4916C200C7F152 /* WebProcessPoolMessages.h */; };
+		7CEFA9621AC0999300B910FD /* APIUserContentExtensionStoreCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7CEFA9601AC0999300B910FD /* APIUserContentExtensionStoreCocoa.mm */; };
 		7CF47FF617275B71008ACB91 /* WKBundlePageBanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CF47FF417275B71008ACB91 /* WKBundlePageBanner.cpp */; };
 		7CF47FF717275B71008ACB91 /* WKBundlePageBanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CF47FF517275B71008ACB91 /* WKBundlePageBanner.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7CF47FFB17275C57008ACB91 /* PageBanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CF47FF917275C57008ACB91 /* PageBanner.h */; };
@@ -3345,6 +3347,7 @@
 		7C8EB11618DB6A19007917C2 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = "<group>"; };
 		7C9D1536184584DA009D3918 /* WKBrowsingContextGroupInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKBrowsingContextGroupInternal.h; sourceTree = "<group>"; };
 		7CA254EA182993CE00FC8A41 /* WKBrowsingContextPolicyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKBrowsingContextPolicyDelegate.h; sourceTree = "<group>"; };
+		7CA3793D1AC378B30079DC37 /* _WKUserContentExtensionStorePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKUserContentExtensionStorePrivate.h; sourceTree = "<group>"; };
 		7CB16FE21724B9B5007A0A95 /* com.apple.ist.ds.appleconnect.webplugin.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.ist.ds.appleconnect.webplugin.sb; sourceTree = "<group>"; };
 		7CB16FE31724B9B5007A0A95 /* com.apple.QuickTime Plugin.plugin.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "com.apple.QuickTime Plugin.plugin.sb"; sourceTree = "<group>"; };
 		7CB16FE41724B9B5007A0A95 /* com.apple.WebKit.plugin-common.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "com.apple.WebKit.plugin-common.sb"; sourceTree = "<group>"; };
@@ -3377,6 +3380,7 @@
 		7CE4D21E1A4914CA00C7F152 /* APIProcessPoolConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APIProcessPoolConfiguration.h; path = UIProcess/API/APIProcessPoolConfiguration.h; sourceTree = SOURCE_ROOT; };
 		7CE4D2251A4916C200C7F152 /* WebProcessPoolMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebProcessPoolMessageReceiver.cpp; sourceTree = "<group>"; };
 		7CE4D2261A4916C200C7F152 /* WebProcessPoolMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebProcessPoolMessages.h; sourceTree = "<group>"; };
+		7CEFA9601AC0999300B910FD /* APIUserContentExtensionStoreCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = APIUserContentExtensionStoreCocoa.mm; sourceTree = "<group>"; };
 		7CF47FF417275B71008ACB91 /* WKBundlePageBanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKBundlePageBanner.cpp; sourceTree = "<group>"; };
 		7CF47FF517275B71008ACB91 /* WKBundlePageBanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKBundlePageBanner.h; sourceTree = "<group>"; };
 		7CF47FF917275C57008ACB91 /* PageBanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageBanner.h; sourceTree = "<group>"; };
@@ -5343,6 +5347,7 @@
 				7C2413011AACFA7500A58C15 /* _WKUserContentExtensionStore.h */,
 				7C2413001AACFA7500A58C15 /* _WKUserContentExtensionStore.mm */,
 				7C2413041AACFA9C00A58C15 /* _WKUserContentExtensionStoreInternal.h */,
+				7CA3793D1AC378B30079DC37 /* _WKUserContentExtensionStorePrivate.h */,
 				7C89D2B81A6B0F2C003A5FDE /* _WKUserContentFilter.h */,
 				7C89D2B71A6B0F2C003A5FDE /* _WKUserContentFilter.mm */,
 				7C89D2BB1A6B0F5B003A5FDE /* _WKUserContentFilterInternal.h */,
@@ -5355,6 +5360,7 @@
 				75A8D2C4187CCF9F00C39C9E /* _WKWebsiteDataStore.h */,
 				75A8D2C5187CCF9F00C39C9E /* _WKWebsiteDataStore.mm */,
 				75A8D2D4187D1C0100C39C9E /* _WKWebsiteDataStoreInternal.h */,
+				7CEFA9601AC0999300B910FD /* APIUserContentExtensionStoreCocoa.mm */,
 				1A3635AB1A3145E500ED6197 /* APIWebsiteDataStoreCocoa.mm */,
 				1AFDE64319510B5500C48FFA /* LegacyBundleForClass.mm */,
 				37C4C08B1814AC5C003688B9 /* WKBackForwardList.h */,
@@ -7813,6 +7819,7 @@
 				BC8780FC1161C2B800CC2768 /* PlatformProcessIdentifier.h in Headers */,
 				1A6FB7D311E651E200DB1371 /* Plugin.h in Headers */,
 				31A67E0D165B2A99006CBA66 /* PlugInAutoStartProvider.h in Headers */,
+				7CA3793E1AC378B30079DC37 /* _WKUserContentExtensionStorePrivate.h in Headers */,
 				1A9FBA8D13FF04E600DEED67 /* PluginComplexTextInputState.h in Headers */,
 				1AA56F2911E92BC80061B882 /* PluginController.h in Headers */,
 				1A8EF4CB1252403700F7067F /* PluginControllerProxy.h in Headers */,
@@ -9596,6 +9603,7 @@
 				1A0EC907124C0AB8007EF4A5 /* PluginProcessConnection.cpp in Sources */,
 				1A0EC910124C0AF5007EF4A5 /* PluginProcessConnectionManager.cpp in Sources */,
 				1A7865B916CAC71500ACE83A /* PluginProcessConnectionManagerMessageReceiver.cpp in Sources */,
+				7CEFA9621AC0999300B910FD /* APIUserContentExtensionStoreCocoa.mm in Sources */,
 				1A2BB6D014117B4D000F35D4 /* PluginProcessConnectionMessageReceiver.cpp in Sources */,
 				1A2D90D31281C966001EB962 /* PluginProcessCreationParameters.cpp in Sources */,
 				1AA4792312A59FD9008236C3 /* PluginProcessMac.mm in Sources */,

Modified: trunk/Tools/ChangeLog (182160 => 182161)


--- trunk/Tools/ChangeLog	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Tools/ChangeLog	2015-03-30 23:22:09 UTC (rev 182161)
@@ -1,3 +1,14 @@
+2015-03-30  Sam Weinig  <[email protected]>
+
+        [Content Extensions] Flesh out the UserContentExtensionStore
+        https://bugs.webkit.org/show_bug.cgi?id=143123
+
+        Reviewed by Benjamin Poulain.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/_WKUserContentExtensionStore.mm: Added.
+        Add tests for _WKUserContentExtensionStore.
+
 2015-03-30  Marcos Chavarría Teijeiro  <[email protected]>
 
         Update install-dependencies Fedora packages after r181624

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (182160 => 182161)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2015-03-30 23:01:51 UTC (rev 182160)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2015-03-30 23:22:09 UTC (rev 182161)
@@ -235,6 +235,7 @@
 		7CCE7F4D1A411B9F00447C4C /* RetainPtr.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC029B1B1486B25900817DA9 /* RetainPtr.mm */; };
 		7CCE7F4E1A411BA400447C4C /* RetainPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC029B161486AD6400817DA9 /* RetainPtr.cpp */; };
 		7CCE7F4F1A411BA400447C4C /* RetainPtrHashing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0991C50143C7D68007998F2 /* RetainPtrHashing.cpp */; };
+		7CEFA9661AC0B9E200B910FD /* _WKUserContentExtensionStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */; };
 		7CFBCAE51743238F00B2BFCF /* WillLoad_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CFBCAE31743238E00B2BFCF /* WillLoad_Bundle.cpp */; };
 		930AD402150698D00067970F /* lots-of-text.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 930AD401150698B30067970F /* lots-of-text.html */; };
 		9361002914DC95A70061379D /* lots-of-iframes.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9361002814DC957B0061379D /* lots-of-iframes.html */; };
@@ -533,6 +534,7 @@
 		7CC3E1FA197E234100BE6252 /* UserContentController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UserContentController.mm; sourceTree = "<group>"; };
 		7CCE7E8C1A41144E00447C4C /* libTestWebKitAPI.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTestWebKitAPI.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		7CCE7EA31A4115CB00447C4C /* TestWebKitAPILibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestWebKitAPILibrary.xcconfig; sourceTree = "<group>"; };
+		7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKUserContentExtensionStore.mm; sourceTree = "<group>"; };
 		7CFBCADD1743234F00B2BFCF /* WillLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WillLoad.cpp; sourceTree = "<group>"; };
 		7CFBCAE31743238E00B2BFCF /* WillLoad_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WillLoad_Bundle.cpp; sourceTree = "<group>"; };
 		81B50192140F232300D9EB58 /* StringBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringBuilder.cpp; sourceTree = "<group>"; };
@@ -805,6 +807,7 @@
 		1ABC3DEC1899BE55004F0626 /* WebKit2 Cocoa */ = {
 			isa = PBXGroup;
 			children = (
+				7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */,
 				A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
 				1ABC3DED1899BE6D004F0626 /* Navigation.mm */,
 				CEA6CF2219CCF5BD0064F5A7 /* OpenAndCloseWindow.mm */,
@@ -1404,6 +1407,7 @@
 				7CCE7F231A411AF600447C4C /* Download.mm in Sources */,
 				7CCE7EEE1A411AE600447C4C /* DownloadDecideDestinationCrash.cpp in Sources */,
 				7CCE7EBE1A411A7E00447C4C /* DynamicDeviceScaleFactor.mm in Sources */,
+				7CEFA9661AC0B9E200B910FD /* _WKUserContentExtensionStore.mm in Sources */,
 				7CCE7EE01A411A9A00447C4C /* EditorCommands.mm in Sources */,
 				7CCE7EBF1A411A7E00447C4C /* ElementAtPointInWebFrame.mm in Sources */,
 				7CCE7EEF1A411AE600447C4C /* EphemeralSessionPushStateNoHistoryCallback.cpp in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/_WKUserContentExtensionStore.mm (0 => 182161)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/_WKUserContentExtensionStore.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/_WKUserContentExtensionStore.mm	2015-03-30 23:22:09 UTC (rev 182161)
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#import "config.h"
+#import <WebKit/WKFoundation.h>
+
+#if WK_API_ENABLED
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import <WebKit/_WKUserContentExtensionStorePrivate.h>
+#import <WebKit/_WKUserContentFilter.h>
+#import <wtf/RetainPtr.h>
+
+class _WKUserContentExtensionStoreTest : public testing::Test {
+public:
+    virtual void SetUp()
+    {
+        [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
+    }
+};
+
+static NSString *basicFilter = @"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*webkit.org\"}}]";
+
+TEST_F(_WKUserContentExtensionStoreTest, Compile)
+{
+    __block bool doneCompiling = false;
+    [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"TestExtension" encodedContentExtension:basicFilter completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&doneCompiling);
+}
+
+static NSString *invalidFilter = @"[";
+
+TEST_F(_WKUserContentExtensionStoreTest, InvalidExtension)
+{
+    __block bool doneCompiling = false;
+    [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"TestExtension" encodedContentExtension:invalidFilter completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NULL(filter);
+        EXPECT_NOT_NULL(error);
+
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&doneCompiling);
+}
+
+TEST_F(_WKUserContentExtensionStoreTest, Lookup)
+{
+    __block bool doneCompiling = false;
+    [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"TestExtension" encodedContentExtension:basicFilter completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&doneCompiling);
+
+    __block bool doneLookingUp = false;
+    [[_WKUserContentExtensionStore defaultStore] lookupContentExtensionForIdentifier:@"TestExtension" completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+
+        doneLookingUp = true;
+    }];
+    TestWebKitAPI::Util::run(&doneLookingUp);
+}
+
+TEST_F(_WKUserContentExtensionStoreTest, NonExistingIdentifierLookup)
+{
+    __block bool doneLookingUp = false;
+    [[_WKUserContentExtensionStore defaultStore] lookupContentExtensionForIdentifier:@"DoesNotExist" completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NULL(filter);
+        EXPECT_NOT_NULL(error);
+
+        doneLookingUp = true;
+    }];
+    TestWebKitAPI::Util::run(&doneLookingUp);
+}
+
+TEST_F(_WKUserContentExtensionStoreTest, Removal)
+{
+    __block bool doneCompiling = false;
+    [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"TestExtension" encodedContentExtension:basicFilter completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
+    
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&doneCompiling);
+
+    __block bool doneRemoving = false;
+    [[_WKUserContentExtensionStore defaultStore] removeContentExtensionForIdentifier:@"TestExtension" completionHandler:^(NSError *error) {
+        EXPECT_NULL(error);
+
+        doneRemoving = true;
+    }];
+    TestWebKitAPI::Util::run(&doneRemoving);
+}
+
+TEST_F(_WKUserContentExtensionStoreTest, NonExistingIdentifierRemove)
+{
+    __block bool doneRemoving = false;
+    [[_WKUserContentExtensionStore defaultStore] removeContentExtensionForIdentifier:@"DoesNotExist" completionHandler:^(NSError *error) {
+        EXPECT_NOT_NULL(error);
+
+        doneRemoving = true;
+    }];
+    TestWebKitAPI::Util::run(&doneRemoving);
+}
+
+#endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to