<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[213988] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/213988">213988</a></dd>
<dt>Author</dt> <dd>achristensen@apple.com</dd>
<dt>Date</dt> <dd>2017-03-15 10:59:08 -0700 (Wed, 15 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Compiled content extensions should include the JSON source
https://bugs.webkit.org/show_bug.cgi?id=169643

Reviewed by Geoffrey Garen.

Source/WebCore:

Serializing the JSON string from which a content extension was compiled
to disk with the compiled content extension will allow us to validate content
extensions and automatically migrate older content extensions to new versions.
It less than doubles the size of the compiled content extension on disk, and when
interpreting the bytecode that memory is never read, so it doesn't increase our
dirty memory usage.

Covered by new API tests.

* contentextensions/ContentExtensionCompiler.cpp:
(WebCore::ContentExtensions::compileRuleList):
* contentextensions/ContentExtensionCompiler.h:

Source/WebKit2:

* UIProcess/API/APIContentExtensionStore.cpp:
(API::ContentExtensionStore::ContentExtensionStore):
(API::ContentExtensionMetaData::fileSize):
(API::encodeContentExtensionMetaData):
(API::decodeContentExtensionMetaData):
(API::compiledToFile):
(API::createExtension):
(API::ContentExtensionStore::getContentExtensionSource):
* UIProcess/API/APIContentExtensionStore.h:
* UIProcess/API/Cocoa/WKContentExtensionStore.mm:
(toWKErrorCode):
(-[WKContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
(-[WKContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):
(-[WKContentExtensionStore _getContentExtensionSourceForIdentifier:completionHandler:]):
* UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h:
* UIProcess/API/Cocoa/WKError.h:
* UIProcess/API/Cocoa/_WKUserContentExtensionStore.h:
* UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm:
(toUserContentExtensionStoreError):
(-[_WKUserContentExtensionStore compileContentExtensionForIdentifier:encodedContentExtension:completionHandler:]):
(-[_WKUserContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
(-[_WKUserContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):

Tools:

* TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
* TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm:
(TEST_F):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsContentExtensionCompilercpp">trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsContentExtensionCompilerh">trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIAPIContentExtensionStorecpp">trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIAPIContentExtensionStoreh">trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKContentExtensionStoremm">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStore.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKContentExtensionStorePrivateh">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKErrorh">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKError.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoa_WKUserContentExtensionStoreh">trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoa_WKUserContentExtensionStoremm">trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreContentExtensionscpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2CocoaWKUserContentExtensionStoremm">trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebCore/ChangeLog        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2017-03-15  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        Compiled content extensions should include the JSON source
+        https://bugs.webkit.org/show_bug.cgi?id=169643
+
+        Reviewed by Geoffrey Garen.
+
+        Serializing the JSON string from which a content extension was compiled
+        to disk with the compiled content extension will allow us to validate content
+        extensions and automatically migrate older content extensions to new versions.
+        It less than doubles the size of the compiled content extension on disk, and when
+        interpreting the bytecode that memory is never read, so it doesn't increase our
+        dirty memory usage.
+
+        Covered by new API tests.
+
+        * contentextensions/ContentExtensionCompiler.cpp:
+        (WebCore::ContentExtensions::compileRuleList):
+        * contentextensions/ContentExtensionCompiler.h:
+
</ins><span class="cx"> 2017-03-15  Antoine Quint  &lt;graouts@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Modern Media Controls] Captions do not default to Auto when language is changed
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsContentExtensionCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -306,6 +306,8 @@
</span><span class="cx"> #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
</span><span class="cx">     double patternPartitioningStart = monotonicallyIncreasingTime();
</span><span class="cx"> #endif
</span><ins>+    
+    client.writeSource(ruleJSON);
</ins><span class="cx"> 
</span><span class="cx">     Vector&lt;SerializedActionByte&gt; actions;
</span><span class="cx">     Vector&lt;unsigned&gt; actionLocations = serializeActions(parsedRuleList, actions);
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsContentExtensionCompilerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -40,6 +40,7 @@
</span><span class="cx">     virtual ~ContentExtensionCompilationClient() { }
</span><span class="cx">     
</span><span class="cx">     // Functions should be called in this order. All except writeActions and finalize can be called multiple times, though.
</span><ins>+    virtual void writeSource(const String&amp;) = 0;
</ins><span class="cx">     virtual void writeActions(Vector&lt;SerializedActionByte&gt;&amp;&amp;, bool conditionsApplyOnlyToDomain) = 0;
</span><span class="cx">     virtual void writeFiltersWithoutConditionsBytecode(Vector&lt;DFABytecode&gt;&amp;&amp;) = 0;
</span><span class="cx">     virtual void writeFiltersWithConditionsBytecode(Vector&lt;DFABytecode&gt;&amp;&amp;) = 0;
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/ChangeLog        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -1,3 +1,33 @@
</span><ins>+2017-03-15  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        Compiled content extensions should include the JSON source
+        https://bugs.webkit.org/show_bug.cgi?id=169643
+
+        Reviewed by Geoffrey Garen.
+
+        * UIProcess/API/APIContentExtensionStore.cpp:
+        (API::ContentExtensionStore::ContentExtensionStore):
+        (API::ContentExtensionMetaData::fileSize):
+        (API::encodeContentExtensionMetaData):
+        (API::decodeContentExtensionMetaData):
+        (API::compiledToFile):
+        (API::createExtension):
+        (API::ContentExtensionStore::getContentExtensionSource):
+        * UIProcess/API/APIContentExtensionStore.h:
+        * UIProcess/API/Cocoa/WKContentExtensionStore.mm:
+        (toWKErrorCode):
+        (-[WKContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
+        (-[WKContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):
+        (-[WKContentExtensionStore _getContentExtensionSourceForIdentifier:completionHandler:]):
+        * UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h:
+        * UIProcess/API/Cocoa/WKError.h:
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStore.h:
+        * UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm:
+        (toUserContentExtensionStoreError):
+        (-[_WKUserContentExtensionStore compileContentExtensionForIdentifier:encodedContentExtension:completionHandler:]):
+        (-[_WKUserContentExtensionStore lookupContentExtensionForIdentifier:completionHandler:]):
+        (-[_WKUserContentExtensionStore removeContentExtensionForIdentifier:completionHandler:]):
+
</ins><span class="cx"> 2017-03-15  Dean Jackson  &lt;dino@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Sort Xcode project files
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIAPIContentExtensionStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.cpp (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.cpp        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.cpp        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -68,6 +68,7 @@
</span><span class="cx">     , m_readQueue(WorkQueue::create(&quot;ContentExtensionStore Read Queue&quot;))
</span><span class="cx">     , m_removeQueue(WorkQueue::create(&quot;ContentExtensionStore Remove Queue&quot;))
</span><span class="cx"> {
</span><ins>+    WebCore::makeAllDirectories(storePath);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ContentExtensionStore::~ContentExtensionStore()
</span><span class="lines">@@ -81,11 +82,12 @@
</span><span class="cx"> 
</span><span class="cx"> // The size and offset of the densely packed bytes in the file, not sizeof and offsetof, which would
</span><span class="cx"> // represent the size and offset of the structure in memory, possibly with compiler-added padding.
</span><del>-const size_t ContentExtensionFileHeaderSize = 2 * sizeof(uint32_t) + 4 * sizeof(uint64_t);
-const size_t ConditionsApplyOnlyToDomainOffset = sizeof(uint32_t) + 4 * sizeof(uint64_t);
</del><ins>+const size_t ContentExtensionFileHeaderSize = 2 * sizeof(uint32_t) + 5 * sizeof(uint64_t);
+const size_t ConditionsApplyOnlyToDomainOffset = sizeof(uint32_t) + 5 * sizeof(uint64_t);
</ins><span class="cx"> 
</span><span class="cx"> struct ContentExtensionMetaData {
</span><span class="cx">     uint32_t version { ContentExtensionStore::CurrentContentExtensionFileVersion };
</span><ins>+    uint64_t sourceSize { 0 };
</ins><span class="cx">     uint64_t actionsSize { 0 };
</span><span class="cx">     uint64_t filtersWithoutConditionsBytecodeSize { 0 };
</span><span class="cx">     uint64_t filtersWithConditionsBytecodeSize { 0 };
</span><span class="lines">@@ -95,6 +97,7 @@
</span><span class="cx">     size_t fileSize() const
</span><span class="cx">     {
</span><span class="cx">         return ContentExtensionFileHeaderSize
</span><ins>+            + sourceSize
</ins><span class="cx">             + actionsSize
</span><span class="cx">             + filtersWithoutConditionsBytecodeSize
</span><span class="cx">             + filtersWithConditionsBytecodeSize
</span><span class="lines">@@ -107,6 +110,7 @@
</span><span class="cx">     WTF::Persistence::Encoder encoder;
</span><span class="cx"> 
</span><span class="cx">     encoder &lt;&lt; metaData.version;
</span><ins>+    encoder &lt;&lt; metaData.sourceSize;
</ins><span class="cx">     encoder &lt;&lt; metaData.actionsSize;
</span><span class="cx">     encoder &lt;&lt; metaData.filtersWithoutConditionsBytecodeSize;
</span><span class="cx">     encoder &lt;&lt; metaData.filtersWithConditionsBytecodeSize;
</span><span class="lines">@@ -129,6 +133,8 @@
</span><span class="cx">         WTF::Persistence::Decoder decoder(data, size);
</span><span class="cx">         if (!decoder.decode(metaData.version))
</span><span class="cx">             return false;
</span><ins>+        if (!decoder.decode(metaData.sourceSize))
+            return false;
</ins><span class="cx">         if (!decoder.decode(metaData.actionsSize))
</span><span class="cx">             return false;
</span><span class="cx">         if (!decoder.decode(metaData.filtersWithoutConditionsBytecodeSize))
</span><span class="lines">@@ -181,6 +187,7 @@
</span><span class="cx">             : m_fileHandle(fileHandle)
</span><span class="cx">             , m_metaData(metaData)
</span><span class="cx">         {
</span><ins>+            ASSERT(!metaData.sourceSize);
</ins><span class="cx">             ASSERT(!metaData.actionsSize);
</span><span class="cx">             ASSERT(!metaData.filtersWithoutConditionsBytecodeSize);
</span><span class="cx">             ASSERT(!metaData.filtersWithConditionsBytecodeSize);
</span><span class="lines">@@ -188,6 +195,25 @@
</span><span class="cx">             ASSERT(!metaData.conditionsApplyOnlyToDomain);
</span><span class="cx">         }
</span><span class="cx">         
</span><ins>+        void writeSource(const String&amp; sourceJSON) final {
+            ASSERT(!m_filtersWithoutConditionsBytecodeWritten);
+            ASSERT(!m_filtersWithConditionBytecodeWritten);
+            ASSERT(!m_conditionFiltersBytecodeWritten);
+            ASSERT(!m_actionsWritten);
+            ASSERT(!m_sourceWritten);
+            writeToFile(sourceJSON.is8Bit());
+            m_sourceWritten += sizeof(bool);
+            if (sourceJSON.is8Bit()) {
+                size_t serializedLength = sourceJSON.length() * sizeof(LChar);
+                writeToFile(Data(sourceJSON.characters8(), serializedLength));
+                m_sourceWritten += serializedLength;
+            } else {
+                size_t serializedLength = sourceJSON.length() * sizeof(UChar);
+                writeToFile(Data(reinterpret_cast&lt;const uint8_t*&gt;(sourceJSON.characters16()), serializedLength));
+                m_sourceWritten += serializedLength;
+            }
+        }
+        
</ins><span class="cx">         void writeFiltersWithoutConditionsBytecode(Vector&lt;DFABytecode&gt;&amp;&amp; bytecode) final
</span><span class="cx">         {
</span><span class="cx">             ASSERT(!m_filtersWithConditionBytecodeWritten);
</span><span class="lines">@@ -222,6 +248,7 @@
</span><span class="cx">         
</span><span class="cx">         void finalize() final
</span><span class="cx">         {
</span><ins>+            m_metaData.sourceSize = m_sourceWritten;
</ins><span class="cx">             m_metaData.actionsSize = m_actionsWritten;
</span><span class="cx">             m_metaData.filtersWithoutConditionsBytecodeSize = m_filtersWithoutConditionsBytecodeWritten;
</span><span class="cx">             m_metaData.filtersWithConditionsBytecodeSize = m_filtersWithConditionBytecodeWritten;
</span><span class="lines">@@ -239,6 +266,10 @@
</span><span class="cx">         bool hadErrorWhileWritingToFile() { return m_fileError; }
</span><span class="cx"> 
</span><span class="cx">     private:
</span><ins>+        void writeToFile(bool value)
+        {
+            writeToFile(Data(reinterpret_cast&lt;const uint8_t*&gt;(&amp;value), sizeof(value)));
+        }
</ins><span class="cx">         void writeToFile(const Data&amp; data)
</span><span class="cx">         {
</span><span class="cx">             if (!m_fileError &amp;&amp; !writeDataToFile(data, m_fileHandle)) {
</span><span class="lines">@@ -253,6 +284,7 @@
</span><span class="cx">         size_t m_filtersWithConditionBytecodeWritten { 0 };
</span><span class="cx">         size_t m_conditionFiltersBytecodeWritten { 0 };
</span><span class="cx">         size_t m_actionsWritten { 0 };
</span><ins>+        size_t m_sourceWritten { 0 };
</ins><span class="cx">         bool m_conditionsApplyOnlyToDomain { false };
</span><span class="cx">         bool m_fileError { false };
</span><span class="cx">     };
</span><span class="lines">@@ -294,20 +326,21 @@
</span><span class="cx"> static RefPtr&lt;API::ContentExtension&gt; createExtension(const String&amp; identifier, const ContentExtensionMetaData&amp; metaData, const Data&amp; fileData)
</span><span class="cx"> {
</span><span class="cx">     auto sharedMemory = WebKit::SharedMemory::create(const_cast&lt;uint8_t*&gt;(fileData.data()), fileData.size(), WebKit::SharedMemory::Protection::ReadOnly);
</span><ins>+    const size_t headerAndSourceSize = ContentExtensionFileHeaderSize + metaData.sourceSize;
</ins><span class="cx">     auto compiledContentExtensionData = WebKit::WebCompiledContentExtensionData(
</span><span class="cx">         WTFMove(sharedMemory),
</span><span class="cx">         fileData,
</span><span class="cx">         ConditionsApplyOnlyToDomainOffset,
</span><del>-        ContentExtensionFileHeaderSize,
</del><ins>+        headerAndSourceSize,
</ins><span class="cx">         metaData.actionsSize,
</span><del>-        ContentExtensionFileHeaderSize
</del><ins>+        headerAndSourceSize
</ins><span class="cx">             + metaData.actionsSize,
</span><span class="cx">         metaData.filtersWithoutConditionsBytecodeSize,
</span><del>-        ContentExtensionFileHeaderSize
</del><ins>+        headerAndSourceSize
</ins><span class="cx">             + metaData.actionsSize
</span><span class="cx">             + metaData.filtersWithoutConditionsBytecodeSize,
</span><span class="cx">         metaData.filtersWithConditionsBytecodeSize,
</span><del>-        ContentExtensionFileHeaderSize
</del><ins>+        headerAndSourceSize
</ins><span class="cx">             + metaData.actionsSize
</span><span class="cx">             + metaData.filtersWithoutConditionsBytecodeSize
</span><span class="cx">             + metaData.filtersWithConditionsBytecodeSize,
</span><span class="lines">@@ -401,7 +434,48 @@
</span><span class="cx">     ASSERT_UNUSED(bytesWritten, bytesWritten == sizeof(invalidHeader));
</span><span class="cx">     WebCore::closeFile(file);
</span><span class="cx"> }
</span><del>-    
</del><ins>+
+void ContentExtensionStore::getContentExtensionSource(const WTF::String&amp; identifier, Function&lt;void(WTF::String)&gt; completionHandler)
+{
+    m_readQueue-&gt;dispatch([protectedThis = makeRef(*this), identifier = identifier.isolatedCopy(), storePath = m_storePath.isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable {
+        auto path = constructedPath(storePath, identifier);
+        
+        auto complete = [protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)](String source) mutable {
+            RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), source = source.isolatedCopy()] {
+                completionHandler(source);
+            });
+        };
+        
+        ContentExtensionMetaData metaData;
+        Data fileData;
+        if (!openAndMapContentExtension(path, metaData, fileData)) {
+            complete({ });
+            return;
+        }
+        
+        switch (metaData.version) {
+        case 9:
+            if (!metaData.sourceSize) {
+                complete({ });
+                return;
+            }
+            bool is8Bit = fileData.data()[ContentExtensionFileHeaderSize];
+            size_t start = ContentExtensionFileHeaderSize + sizeof(bool);
+            size_t length = metaData.sourceSize - sizeof(bool);
+            if (is8Bit)
+                complete(String(fileData.data() + start, length));
+            else {
+                ASSERT(!(length % sizeof(UChar)));
+                complete(String(reinterpret_cast&lt;const UChar*&gt;(fileData.data() + start), length / sizeof(UChar)));
+            }
+            return;
+        }
+
+        // Older versions cannot recover the original JSON source from disk.
+        complete({ });
+    });
+}
+
</ins><span class="cx"> const std::error_category&amp; contentExtensionStoreErrorCategory()
</span><span class="cx"> {
</span><span class="cx">     class ContentExtensionStoreErrorCategory : public std::error_category {
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIAPIContentExtensionStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.h (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.h        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/APIContentExtensionStore.h        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> class ContentExtensionStore final : public ObjectImpl&lt;Object::Type::ContentExtensionStore&gt; {
</span><span class="cx"> public:
</span><span class="cx">     enum class Error {
</span><del>-        LookupFailed = 6, // Mirrors value of WKErrorContentExtensionStoreLookupFailed
</del><ins>+        LookupFailed = 1,
</ins><span class="cx">         VersionMismatch,
</span><span class="cx">         CompileFailed,
</span><span class="cx">         RemoveFailed
</span><span class="lines">@@ -50,7 +50,9 @@
</span><span class="cx">     
</span><span class="cx">     // This should be incremented every time a functional change is made to the bytecode, file format, etc.
</span><span class="cx">     // to prevent crashing while loading old data.
</span><del>-    const static uint32_t CurrentContentExtensionFileVersion = 8;
</del><ins>+    // Also update ContentExtensionStore::getContentExtensionSource to be able to find the original JSON
+    // source from old versions.
+    const static uint32_t CurrentContentExtensionFileVersion = 9;
</ins><span class="cx"> 
</span><span class="cx">     static ContentExtensionStore&amp; defaultStore();
</span><span class="cx">     static Ref&lt;ContentExtensionStore&gt; storeWithPath(const WTF::String&amp; storePath);
</span><span class="lines">@@ -66,6 +68,7 @@
</span><span class="cx">     // For testing only.
</span><span class="cx">     void synchronousRemoveAllContentExtensions();
</span><span class="cx">     void invalidateContentExtensionVersion(const WTF::String&amp; identifier);
</span><ins>+    void getContentExtensionSource(const WTF::String&amp; identifier, Function&lt;void(WTF::String)&gt;);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     WTF::String defaultStorePath();
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKContentExtensionStoremm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStore.mm (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStore.mm        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStore.mm        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -29,8 +29,26 @@
</span><span class="cx"> 
</span><span class="cx"> #if WK_API_ENABLED
</span><span class="cx"> 
</span><ins>+#import &quot;APIContentExtensionStore.h&quot;
</ins><span class="cx"> #import &quot;WKErrorInternal.h&quot;
</span><span class="cx"> 
</span><ins>+static WKErrorCode toWKErrorCode(const std::error_code&amp; error)
+{
+    ASSERT(error.category() == API::contentExtensionStoreErrorCategory());
+    switch (static_cast&lt;API::ContentExtensionStore::Error&gt;(error.value())) {
+    case API::ContentExtensionStore::Error::LookupFailed:
+        return WKErrorContentExtensionStoreLookupFailed;
+    case API::ContentExtensionStore::Error::VersionMismatch:
+        return WKErrorContentExtensionStoreVersionMismatch;
+    case API::ContentExtensionStore::Error::CompileFailed:
+        return WKErrorContentExtensionStoreCompileFailed;
+    case API::ContentExtensionStore::Error::RemoveFailed:
+        return WKErrorContentExtensionStoreRemoveFailed;
+    }
+    ASSERT_NOT_REACHED();
+    return WKErrorUnknown;
+}
+
</ins><span class="cx"> @implementation WKContentExtensionStore
</span><span class="cx"> 
</span><span class="cx"> - (void)dealloc
</span><span class="lines">@@ -92,8 +110,9 @@
</span><span class="cx">             auto rawHandler = (void (^)(WKContentExtension *, NSError *))handler.get();
</span><span class="cx"> 
</span><span class="cx">             auto userInfo = @{NSHelpAnchorErrorKey: [NSString stringWithFormat:@&quot;Extension lookup failed: %s&quot;, error.message().c_str()]};
</span><del>-            ASSERT(error.value() == WKErrorContentExtensionStoreLookupFailed || error.value() == WKErrorContentExtensionStoreVersionMismatch);
-            rawHandler(nil, [NSError errorWithDomain:WKErrorDomain code:error.value() userInfo:userInfo]);
</del><ins>+            auto wkError = toWKErrorCode(error);
+            ASSERT(wkError == WKErrorContentExtensionStoreLookupFailed || wkError == WKErrorContentExtensionStoreVersionMismatch);
+            rawHandler(nil, [NSError errorWithDomain:WKErrorDomain code:wkError userInfo:userInfo]);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -111,7 +130,7 @@
</span><span class="cx">             auto rawHandler = (void (^)(NSError *))handler.get();
</span><span class="cx"> 
</span><span class="cx">             auto userInfo = @{NSHelpAnchorErrorKey: [NSString stringWithFormat:@&quot;Extension removal failed: %s&quot;, error.message().c_str()]};
</span><del>-            ASSERT(error.value() == WKErrorContentExtensionStoreRemoveFailed);
</del><ins>+            ASSERT(toWKErrorCode(error) == WKErrorContentExtensionStoreRemoveFailed);
</ins><span class="cx">             rawHandler([NSError errorWithDomain:WKErrorDomain code:WKErrorContentExtensionStoreRemoveFailed userInfo:userInfo]);
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="lines">@@ -144,6 +163,18 @@
</span><span class="cx">     _contentExtensionStore-&gt;invalidateContentExtensionVersion(identifier);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_getContentExtensionSourceForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSString*))completionHandler
+{
+    auto handler = adoptNS([completionHandler copy]);
+    _contentExtensionStore-&gt;getContentExtensionSource(identifier, [handler](String source) {
+        auto rawHandler = (void (^)(NSString *))handler.get();
+        if (source.isNull())
+            rawHandler(nil);
+        else
+            rawHandler(source);
+    });
+}
+
</ins><span class="cx"> // NS_RELEASES_ARGUMENT to keep peak memory usage low.
</span><span class="cx"> 
</span><span class="cx"> - (void)_compileContentExtensionForIdentifier:(NSString *)identifier encodedContentExtension:(NSString *)encodedContentExtension completionHandler:(void (^)(WKContentExtension *, NSError *))completionHandler
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKContentExtensionStorePrivateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKContentExtensionStorePrivate.h        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> // For testing only.
</span><span class="cx"> - (void)_removeAllContentExtensions;
</span><span class="cx"> - (void)_invalidateContentExtensionVersionForIdentifier:(NSString *)identifier;
</span><ins>+- (void)_getContentExtensionSourceForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSString*))completionHandler;
</ins><span class="cx"> 
</span><span class="cx"> // NS_RELEASES_ARGUMENT to keep peak memory usage low.
</span><span class="cx"> - (void)_compileContentExtensionForIdentifier:(NSString *)identifier encodedContentExtension:(NSString *) NS_RELEASES_ARGUMENT encodedContentExtension completionHandler:(void (^)(WKContentExtension *, NSError *))completionHandler;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKErrorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKError.h (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKError.h        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKError.h        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx">  @constant WKErrorJavaScriptExceptionOccurred          Indicates that a JavaScript exception occurred.
</span><span class="cx">  @constant WKErrorJavaScriptResultTypeIsUnsupported    Indicates that the result of JavaScript execution could not be returned.
</span><span class="cx">  @constant WKErrorContentExtensionStoreLookupFailed    Indicates that looking up a WKUserContentExtension failed.
</span><del>- @constant WKErrorContentExtensionStoreVersionMismatch Indicates that looking up a WKUserContentExtension found an extension with an incompatible binary version.
</del><ins>+ @constant WKErrorContentExtensionStoreVersionMismatch Indicates that the WKUserContentExtension version did not match the latest.
</ins><span class="cx">  @constant WKErrorContentExtensionStoreCompileFailed   Indicates that compiling a WKUserContentExtension failed.
</span><span class="cx">  @constant WKErrorContentExtensionStoreRemoveFailed    Indicates that removing a WKUserContentExtension failed.
</span><span class="cx">  */
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoa_WKUserContentExtensionStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.h        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -44,7 +44,7 @@
</span><span class="cx"> WK_EXTERN NSString * const _WKUserContentExtensionsDomain WK_API_AVAILABLE(macosx(10.12), ios(10.0));
</span><span class="cx"> 
</span><span class="cx"> typedef NS_ENUM(NSInteger, _WKUserContentExtensionStoreErrorCode) {
</span><del>-    _WKUserContentExtensionStoreErrorLookupFailed = 6, // Mirrors value of WKErrorContentExtensionStoreLookupFailed
</del><ins>+    _WKUserContentExtensionStoreErrorLookupFailed,
</ins><span class="cx">     _WKUserContentExtensionStoreErrorVersionMismatch,
</span><span class="cx">     _WKUserContentExtensionStoreErrorCompileFailed,
</span><span class="cx">     _WKUserContentExtensionStoreErrorRemoveFailed,
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoa_WKUserContentExtensionStoremm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/_WKUserContentExtensionStore.mm        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -38,6 +38,26 @@
</span><span class="cx"> 
</span><span class="cx"> NSString * const _WKUserContentExtensionsDomain = @&quot;WKErrorDomain&quot;;
</span><span class="cx"> 
</span><ins>+static NSError *toUserContentExtensionStoreError(const NSError *error)
+{
+    if (!error)
+        return nil;
+
+    ASSERT(error.domain == WKErrorDomain);
+    switch (error.code) {
+    case WKErrorContentExtensionStoreLookupFailed:
+        return [NSError errorWithDomain:_WKUserContentExtensionsDomain code:_WKUserContentExtensionStoreErrorLookupFailed userInfo:error.userInfo];
+    case WKErrorContentExtensionStoreVersionMismatch:
+        return [NSError errorWithDomain:_WKUserContentExtensionsDomain code:_WKUserContentExtensionStoreErrorVersionMismatch userInfo:error.userInfo];
+    case WKErrorContentExtensionStoreCompileFailed:
+        return [NSError errorWithDomain:_WKUserContentExtensionsDomain code:_WKUserContentExtensionStoreErrorCompileFailed userInfo:error.userInfo];
+    case WKErrorContentExtensionStoreRemoveFailed:
+        return [NSError errorWithDomain:_WKUserContentExtensionsDomain code:_WKUserContentExtensionStoreErrorRemoveFailed userInfo:error.userInfo];
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+}
+
</ins><span class="cx"> @implementation _WKUserContentExtensionStore
</span><span class="cx"> 
</span><span class="cx"> + (instancetype)defaultStore
</span><span class="lines">@@ -54,7 +74,7 @@
</span><span class="cx"> {
</span><span class="cx">     [_contentExtensionStore _compileContentExtensionForIdentifier:identifier encodedContentExtension:encodedContentExtension completionHandler:^(WKContentExtension *contentExtension, NSError *error) {
</span><span class="cx">         _WKUserContentFilter *contentFilter = contentExtension ? [[[_WKUserContentFilter alloc] _initWithWKContentExtension:contentExtension] autorelease] : nil;
</span><del>-        completionHandler(contentFilter, error);
</del><ins>+        completionHandler(contentFilter, toUserContentExtensionStoreError(error));
</ins><span class="cx">     }];
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -62,13 +82,15 @@
</span><span class="cx"> {
</span><span class="cx">     [_contentExtensionStore lookupContentExtensionForIdentifier:identifier completionHandler:^(WKContentExtension *contentExtension, NSError *error) {
</span><span class="cx">         _WKUserContentFilter *contentFilter = contentExtension ? [[[_WKUserContentFilter alloc] _initWithWKContentExtension:contentExtension] autorelease] : nil;
</span><del>-        completionHandler(contentFilter, error);
</del><ins>+        completionHandler(contentFilter, toUserContentExtensionStoreError(error));
</ins><span class="cx">     }];
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)removeContentExtensionForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSError *))completionHandler
</span><span class="cx"> {
</span><del>-    [_contentExtensionStore removeContentExtensionForIdentifier:identifier completionHandler:completionHandler];
</del><ins>+    [_contentExtensionStore removeContentExtensionForIdentifier:identifier completionHandler:^(NSError *error) {
+        completionHandler(toUserContentExtensionStoreError(error));
+    }];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #pragma mark WKObject protocol implementation
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Tools/ChangeLog        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2017-03-15  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        Compiled content extensions should include the JSON source
+        https://bugs.webkit.org/show_bug.cgi?id=169643
+
+        Reviewed by Geoffrey Garen.
+
+        * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm:
+        (TEST_F):
+
</ins><span class="cx"> 2017-03-15  Kocsen Chung  &lt;kocsen_chung@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Use git's -C flag when possible in VCSUtils.pm
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreContentExtensionscpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -100,6 +100,8 @@
</span><span class="cx">         EXPECT_EQ(data.topURLFilters.size(), 0ull);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void writeSource(const String&amp;) final { }
+
</ins><span class="cx">     void writeActions(Vector&lt;ContentExtensions::SerializedActionByte&gt;&amp;&amp; actions, bool conditionsApplyOnlyToDomain) final
</span><span class="cx">     {
</span><span class="cx">         EXPECT_FALSE(finalized);
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2CocoaWKUserContentExtensionStoremm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm (213987 => 213988)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm        2017-03-15 17:40:59 UTC (rev 213987)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/WKUserContentExtensionStore.mm        2017-03-15 17:59:08 UTC (rev 213988)
</span><span class="lines">@@ -146,6 +146,13 @@
</span><span class="cx">         doneLookingUp = true;
</span><span class="cx">     }];
</span><span class="cx">     TestWebKitAPI::Util::run(&amp;doneLookingUp);
</span><ins>+
+    __block bool doneGettingSource = false;
+    [[WKContentExtensionStore defaultStore] _getContentExtensionSourceForIdentifier:@&quot;TestExtension&quot; completionHandler:^(NSString* source) {
+        EXPECT_NULL(source);
+        doneGettingSource = true;
+    }];
+    TestWebKitAPI::Util::run(&amp;doneGettingSource);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TEST_F(WKContentExtensionStoreTest, Removal)
</span><span class="lines">@@ -183,4 +190,69 @@
</span><span class="cx">     TestWebKitAPI::Util::run(&amp;doneRemoving);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST_F(WKContentExtensionStoreTest, NonDefaultStore)
+{
+    NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@&quot;ContentExtensionTest&quot;] isDirectory:YES];
+    WKContentExtensionStore *store = [WKContentExtensionStore storeWithURL:tempDir];
+    NSString *identifier = @&quot;TestExtension&quot;;
+    NSString *fileName = @&quot;ContentExtension-TestExtension&quot;;
+
+    __block bool doneCompiling = false;
+    [store compileContentExtensionForIdentifier:identifier encodedContentExtension:basicFilter completionHandler:^(WKContentExtension *filter, NSError *error) {
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+        doneCompiling = true;
+    }];
+    TestWebKitAPI::Util::run(&amp;doneCompiling);
+
+    NSData *data = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
+    EXPECT_NOT_NULL(data);
+    EXPECT_EQ(data.length, 228u);
+    
+    __block bool doneCheckingSource = false;
+    [store _getContentExtensionSourceForIdentifier:identifier completionHandler:^(NSString *source) {
+        EXPECT_NOT_NULL(source);
+        EXPECT_STREQ(basicFilter.UTF8String, source.UTF8String);
+        doneCheckingSource = true;
+    }];
+    TestWebKitAPI::Util::run(&amp;doneCheckingSource);
+    
+    __block bool doneRemoving = false;
+    [store removeContentExtensionForIdentifier:identifier completionHandler:^(NSError *error) {
+        EXPECT_NULL(error);
+        doneRemoving = true;
+    }];
+    TestWebKitAPI::Util::run(&amp;doneRemoving);
+
+    NSData *dataAfterRemoving = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
+    EXPECT_NULL(dataAfterRemoving);
+}
+
+TEST_F(WKContentExtensionStoreTest, NonASCIISource)
+{
+    static NSString *nonASCIIFilter = @&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;.*webkit.org\&quot;}, \&quot;unused\&quot;:\&quot;💩\&quot;}]&quot;;
+    NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@&quot;ContentExtensionTest&quot;] isDirectory:YES];
+    WKContentExtensionStore *store = [WKContentExtensionStore storeWithURL:tempDir];
+    NSString *identifier = @&quot;TestExtension&quot;;
+    NSString *fileName = @&quot;ContentExtension-TestExtension&quot;;
+    
+    __block bool done = false;
+    [store compileContentExtensionForIdentifier:identifier encodedContentExtension:nonASCIIFilter completionHandler:^(WKContentExtension *filter, NSError *error) {
+        EXPECT_NOT_NULL(filter);
+        EXPECT_NULL(error);
+
+        [store _getContentExtensionSourceForIdentifier:identifier completionHandler:^(NSString *source) {
+            EXPECT_NOT_NULL(source);
+            EXPECT_STREQ(nonASCIIFilter.UTF8String, source.UTF8String);
+
+            [store _removeAllContentExtensions];
+            NSData *dataAfterRemoving = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
+            EXPECT_NULL(dataAfterRemoving);
+
+            done = true;
+        }];
+    }];
+    TestWebKitAPI::Util::run(&amp;done);
+}
+
</ins><span class="cx"> #endif
</span></span></pre>
</div>
</div>

</body>
</html>