Title: [195764] trunk
Revision
195764
Author
jer.no...@apple.com
Date
2016-01-28 11:17:17 -0800 (Thu, 28 Jan 2016)

Log Message

Custom protocol loading through AVFoundation does not support byte-range requests.
https://bugs.webkit.org/show_bug.cgi?id=152919
<rdar://problem/23664657>

Reviewed by Alex Christensen.

Source/WebCore:

Tests: http/tests/xmlhttprequest/blob-request-byte-range.html
       TestWebkitAPI/Tests/WebCore/ParsedContentRange.cpp

When loading data through the AVAssetResourceLoaderDelegateProtocol, AVFoundation will issue
requests for specific byte-ranges by adding a "Range:" HTTP header to the NSURLRequest it
passes to the delegate.  WebCore ignores this header, loads the entire resource, and replies
to the callback with the requested subset of the entire resource.

For byte-range requests near the end of a resource, this is inefficient, as the entire
resource up to, and including, the requested range must be loaded before any data can be
returned. Explicitly handle byte-range requests by creating a CachedResourceRequest with the
underlying NSURLRequest (which includes the "Range:" header) rather than just the request's
URL. BlobResourceHandle must be modified to add the "Content-Range:" response header to the
ResourceResponse.

To facilitate both generating and parsing the "Content-Range:" header, add a new
ParsedContentRange class for use by ResourceResponse and its clients. This class provides
methods both for parsing a "Content-Range" header value string, and for generating the
header value from elemental values.

* platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm:
(WebCore::WebCoreAVFResourceLoader::startLoading):
(WebCore::WebCoreAVFResourceLoader::responseReceived):
(WebCore::WebCoreAVFResourceLoader::fulfillRequestWithResource):
* platform/network/BlobResourceHandle.cpp:
(WebCore::BlobResourceHandle::BlobResourceHandle):
(WebCore::BlobResourceHandle::didGetSize):
(WebCore::BlobResourceHandle::seek):
(WebCore::BlobResourceHandle::notifyResponseOnSuccess):
* platform/network/BlobResourceHandle.h:
* platform/network/HTTPHeaderNames.in:
* platform/network/ParsedContentRange.cpp: Added.
(WebCore::areContentRangeValuesValid):
(WebCore::parseContentRange):
(WebCore::ParsedContentRange::ParsedContentRange):
(WebCore::ParsedContentRange::headerValue):
* platform/network/ParsedContentRange.h: Added.
(WebCore::ParsedContentRange::ParsedContentRange):
(WebCore::ParsedContentRange::isValid):
(WebCore::ParsedContentRange::firstBytePosition):
(WebCore::ParsedContentRange::lastBytePosition):
(WebCore::ParsedContentRange::instanceLength):
* platform/network/ResourceResponseBase.cpp:
(WebCore::ResourceResponseBase::updateHeaderParsedState):
(WebCore::parseContentRangeInHeader):
(WebCore::ResourceResponseBase::contentRange):
* platform/network/ResourceResponseBase.h:
* CMakeLists.txt:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:

Tools:

Add tests for new ParsedContntRange class.

* TestWebKitAPI/PlatformWin.cmake:
* TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj:
* TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj.filters:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp: Added.
(TestWebKitAPI::TEST):

Modified Paths

Added Paths

Diff

Added: trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range-expected.txt (0 => 195764)


--- trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range-expected.txt	2016-01-28 19:17:17 UTC (rev 195764)
@@ -0,0 +1,5 @@
+Test an XMLHttpRequest of a Blob URL requesting a byte-range responds appropriately.
+
+PASS: "req.status" == "206"
+PASS: "req.getResponseHeader("Content-Range")" == "bytes 1-2/4"
+

Added: trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range.html (0 => 195764)


--- trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/xmlhttprequest/blob-request-byte-range.html	2016-01-28 19:17:17 UTC (rev 195764)
@@ -0,0 +1,45 @@
+<html>
+<body>
+<p>Test an XMLHttpRequest of a Blob URL requesting a byte-range responds appropriately.</p>
+<pre id="console"></pre>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function log(text)
+{
+    var console = document.getElementById('console');
+    console.appendChild(document.createTextNode(text + '\n'));
+}
+
+function test(expect, actual)
+{
+    var result = eval(actual);
+    if (expect == result)
+        log(`PASS: "${ actual }" == "${ expect }"`);
+    else
+        log(`FAIL: "${ actual }" EXPECT "${ expect }" GOT "${ result }"`);
+}
+
+var array = new Int8Array([0, 1, 2, 3]);
+var blob = new Blob(array);
+var url = ""
+
+var req = new XMLHttpRequest;
+req.responseType = 'blob';
+req.open('GET', url);
+req.setRequestHeader('Range', 'bytes=1-2');
+req._onreadystatechange_ = function() {
+    if (req.readyState == 4) {
+        test(206, 'req.status');
+        test('bytes 1-2/4', 'req.getResponseHeader("Content-Range")');
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+};
+req.send();
+
+</script>
+</body>

Modified: trunk/Source/WebCore/CMakeLists.txt (195763 => 195764)


--- trunk/Source/WebCore/CMakeLists.txt	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/CMakeLists.txt	2016-01-28 19:17:17 UTC (rev 195764)
@@ -2331,6 +2331,7 @@
     platform/network/HTTPParsers.cpp
     platform/network/MIMEHeader.cpp
     platform/network/NetworkStateNotifier.cpp
+    platform/network/ParsedContentRange.cpp
     platform/network/ParsedContentType.cpp
     platform/network/ProtectionSpaceBase.cpp
     platform/network/ProxyServer.cpp

Modified: trunk/Source/WebCore/ChangeLog (195763 => 195764)


--- trunk/Source/WebCore/ChangeLog	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/ChangeLog	2016-01-28 19:17:17 UTC (rev 195764)
@@ -1,3 +1,63 @@
+2016-01-08  Jer Noble  <jer.no...@apple.com>
+
+        Custom protocol loading through AVFoundation does not support byte-range requests.
+        https://bugs.webkit.org/show_bug.cgi?id=152919
+        <rdar://problem/23664657>
+
+        Reviewed by Alex Christensen.
+
+        Tests: http/tests/xmlhttprequest/blob-request-byte-range.html
+               TestWebkitAPI/Tests/WebCore/ParsedContentRange.cpp
+
+        When loading data through the AVAssetResourceLoaderDelegateProtocol, AVFoundation will issue
+        requests for specific byte-ranges by adding a "Range:" HTTP header to the NSURLRequest it
+        passes to the delegate.  WebCore ignores this header, loads the entire resource, and replies
+        to the callback with the requested subset of the entire resource.
+
+        For byte-range requests near the end of a resource, this is inefficient, as the entire
+        resource up to, and including, the requested range must be loaded before any data can be
+        returned. Explicitly handle byte-range requests by creating a CachedResourceRequest with the
+        underlying NSURLRequest (which includes the "Range:" header) rather than just the request's
+        URL. BlobResourceHandle must be modified to add the "Content-Range:" response header to the
+        ResourceResponse. 
+
+        To facilitate both generating and parsing the "Content-Range:" header, add a new
+        ParsedContentRange class for use by ResourceResponse and its clients. This class provides
+        methods both for parsing a "Content-Range" header value string, and for generating the
+        header value from elemental values.
+
+        * platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm:
+        (WebCore::WebCoreAVFResourceLoader::startLoading):
+        (WebCore::WebCoreAVFResourceLoader::responseReceived):
+        (WebCore::WebCoreAVFResourceLoader::fulfillRequestWithResource):
+        * platform/network/BlobResourceHandle.cpp:
+        (WebCore::BlobResourceHandle::BlobResourceHandle):
+        (WebCore::BlobResourceHandle::didGetSize):
+        (WebCore::BlobResourceHandle::seek):
+        (WebCore::BlobResourceHandle::notifyResponseOnSuccess):
+        * platform/network/BlobResourceHandle.h:
+        * platform/network/HTTPHeaderNames.in:
+        * platform/network/ParsedContentRange.cpp: Added.
+        (WebCore::areContentRangeValuesValid):
+        (WebCore::parseContentRange):
+        (WebCore::ParsedContentRange::ParsedContentRange):
+        (WebCore::ParsedContentRange::headerValue):
+        * platform/network/ParsedContentRange.h: Added.
+        (WebCore::ParsedContentRange::ParsedContentRange):
+        (WebCore::ParsedContentRange::isValid):
+        (WebCore::ParsedContentRange::firstBytePosition):
+        (WebCore::ParsedContentRange::lastBytePosition):
+        (WebCore::ParsedContentRange::instanceLength):
+        * platform/network/ResourceResponseBase.cpp:
+        (WebCore::ResourceResponseBase::updateHeaderParsedState):
+        (WebCore::parseContentRangeInHeader):
+        (WebCore::ResourceResponseBase::contentRange):
+        * platform/network/ResourceResponseBase.h:
+        * CMakeLists.txt:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * WebCore.xcodeproj/project.pbxproj:
+
 2016-01-28  Chris Dumez  <cdu...@apple.com>
 
         Storage interface's attributes / operations should be enumerable

Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj (195763 => 195764)


--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj	2016-01-28 19:17:17 UTC (rev 195764)
@@ -8886,6 +8886,7 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\platform\network\ParsedContentRange.cpp" />
     <ClCompile Include="..\platform\network\ParsedContentType.cpp" />
     <ClCompile Include="..\platform\network\ProtectionSpaceBase.cpp" />
     <ClCompile Include="..\platform\network\ProxyServer.cpp" />
@@ -21352,6 +21353,7 @@
     <ClInclude Include="..\platform\network\NetworkingContext.h" />
     <ClInclude Include="..\platform\network\NetworkStateNotifier.h" />
     <ClInclude Include="..\platform\network\NetworkStorageSession.h" />
+    <ClInclude Include="..\platform\network\ParsedContentRange.h" />
     <ClInclude Include="..\platform\network\ParsedContentType.h" />
     <ClInclude Include="..\platform\network\PlatformCookieJar.h" />
     <ClInclude Include="..\platform\network\ProtectionSpace.h" />

Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters (195763 => 195764)


--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters	2016-01-28 19:17:17 UTC (rev 195764)
@@ -1772,6 +1772,9 @@
     <ClCompile Include="..\platform\network\NetworkStorageSessionStub.cpp">
       <Filter>platform\network</Filter>
     </ClCompile>
+    <ClCompile Include="..\platform\network\ParsedContentRange.cpp">
+      <Filter>platform\network</Filter>
+    </ClCompile>
     <ClCompile Include="..\platform\network\ParsedContentType.cpp">
       <Filter>platform\network</Filter>
     </ClCompile>
@@ -8779,6 +8782,9 @@
     <ClInclude Include="..\platform\network\NetworkStorageSession.h">
       <Filter>platform\network</Filter>
     </ClInclude>
+    <ClInclude Include="..\platform\network\ParsedContenRange.h">
+      <Filter>platform\network</Filter>
+    </ClInclude>
     <ClInclude Include="..\platform\network\ParsedContentType.h">
       <Filter>platform\network</Filter>
     </ClInclude>

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (195763 => 195764)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2016-01-28 19:17:17 UTC (rev 195764)
@@ -6097,6 +6097,8 @@
 		CDC979F51C498C0900DB50D4 /* WebCoreNSErrorExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC979F31C498C0900DB50D4 /* WebCoreNSErrorExtras.h */; };
 		CDCA82961679100F00875714 /* TextTrackRepresentationIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDCA82941679100F00875714 /* TextTrackRepresentationIOS.mm */; };
 		CDCA98EB18B2C8EB00C12FF9 /* CDMPrivateMediaPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCA98EA18B2C8EB00C12FF9 /* CDMPrivateMediaPlayer.cpp */; };
+		CDCD41E71C3DDB0900965D99 /* ParsedContentRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCD41E51C3DDB0900965D99 /* ParsedContentRange.cpp */; };
+		CDCD41E81C3DDB0A00965D99 /* ParsedContentRange.h in Headers */ = {isa = PBXBuildFile; fileRef = CDCD41E61C3DDB0900965D99 /* ParsedContentRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CDCFABBD18C0AF78006F8450 /* SelectionSubtreeRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = CDCFABBB18C0AE31006F8450 /* SelectionSubtreeRoot.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CDCFABBE18C0AF84006F8450 /* SelectionSubtreeRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCFABBC18C0AF19006F8450 /* SelectionSubtreeRoot.cpp */; };
 		CDD525D7145B6DD0008D204D /* JSHTMLMediaElementCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDF65CCC145B6AFE00C4C7AA /* JSHTMLMediaElementCustom.cpp */; };
@@ -14009,6 +14011,8 @@
 		CDCA82941679100F00875714 /* TextTrackRepresentationIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextTrackRepresentationIOS.mm; sourceTree = "<group>"; };
 		CDCA98E918B2C8D000C12FF9 /* CDMPrivateMediaPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMPrivateMediaPlayer.h; sourceTree = "<group>"; };
 		CDCA98EA18B2C8EB00C12FF9 /* CDMPrivateMediaPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CDMPrivateMediaPlayer.cpp; sourceTree = "<group>"; };
+		CDCD41E51C3DDB0900965D99 /* ParsedContentRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParsedContentRange.cpp; sourceTree = "<group>"; };
+		CDCD41E61C3DDB0900965D99 /* ParsedContentRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParsedContentRange.h; sourceTree = "<group>"; };
 		CDCE5CD014633BC900D47CCA /* EventTargetFactory.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EventTargetFactory.in; sourceTree = "<group>"; };
 		CDCFABBB18C0AE31006F8450 /* SelectionSubtreeRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectionSubtreeRoot.h; sourceTree = "<group>"; };
 		CDCFABBC18C0AF19006F8450 /* SelectionSubtreeRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectionSubtreeRoot.cpp; sourceTree = "<group>"; };
@@ -17583,6 +17587,8 @@
 				1A7FA61A0DDA3BBE0028F8A5 /* NetworkStateNotifier.cpp */,
 				1A7FA6180DDA3B3A0028F8A5 /* NetworkStateNotifier.h */,
 				E13EF3421684ECF40034C83F /* NetworkStorageSession.h */,
+				CDCD41E51C3DDB0900965D99 /* ParsedContentRange.cpp */,
+				CDCD41E61C3DDB0900965D99 /* ParsedContentRange.h */,
 				447958021643B47B001E0A7F /* ParsedContentType.cpp */,
 				447958031643B47B001E0A7F /* ParsedContentType.h */,
 				51B454E91B4DAE7D0085EAA6 /* PingHandle.h */,
@@ -25671,6 +25677,7 @@
 				939885C408B7E3D100E707C4 /* EventNames.h in Headers */,
 				8F67561B1288B17B0047ACA3 /* EventQueue.h in Headers */,
 				E0FEF372B17C53EAC1C1FBEE /* EventSource.h in Headers */,
+				CDCD41E81C3DDB0A00965D99 /* ParsedContentRange.h in Headers */,
 				E12EDB7B0B308A78002704B6 /* EventTarget.h in Headers */,
 				97AA3CA5145237CC003E1DA6 /* EventTargetHeaders.h in Headers */,
 				97AA3CA6145237CC003E1DA6 /* EventTargetInterfaces.h in Headers */,
@@ -29584,6 +29591,7 @@
 				A454424A119B3661009BE912 /* HTMLMeterElement.cpp in Sources */,
 				A8CFF7A90A156978000A4234 /* HTMLModElement.cpp in Sources */,
 				A8DF3FD5097FA0FC0052981B /* HTMLNameCollection.cpp in Sources */,
+				CDCD41E71C3DDB0900965D99 /* ParsedContentRange.cpp in Sources */,
 				A8D06B3A0A265DCD005E7203 /* HTMLNames.cpp in Sources */,
 				A871D45B0A127CBC00B12A68 /* HTMLObjectElement.cpp in Sources */,
 				A8EA79FB0A1916DF00A8EF5F /* HTMLOListElement.cpp in Sources */,

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm (195763 => 195764)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm	2016-01-28 19:17:17 UTC (rev 195764)
@@ -65,10 +65,10 @@
     if (m_resource || !m_parent)
         return;
 
-    URL requestURL = [[m_avRequest.get() request] URL];
+    NSURLRequest *nsRequest = [m_avRequest.get() request];
 
     // ContentSecurityPolicyImposition::DoPolicyCheck is a placeholder value. It does not affect the request since Content Security Policy does not apply to raw resources.
-    CachedResourceRequest request(ResourceRequest(requestURL), ResourceLoaderOptions(SendCallbacks, DoNotSniffContent, BufferData, DoNotAllowStoredCredentials, DoNotAskClientForCrossOriginCredentials, ClientDidNotRequestCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading));
+    CachedResourceRequest request(nsRequest, ResourceLoaderOptions(SendCallbacks, DoNotSniffContent, BufferData, DoNotAllowStoredCredentials, DoNotAskClientForCrossOriginCredentials, ClientDidNotRequestCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading));
 
     request.mutableResourceRequest().setPriority(ResourceLoadPriority::Low);
     CachedResourceLoader* loader = m_parent->player()->cachedResourceLoader();
@@ -76,7 +76,7 @@
     if (m_resource)
         m_resource->addClient(this);
     else {
-        LOG_ERROR("Failed to start load for media at url %s", requestURL.string().ascii().data());
+        LOG_ERROR("Failed to start load for media at url %s", [[[nsRequest URL] absoluteString] UTF8String]);
         [m_avRequest.get() finishLoadingWithError:0];
     }
 }
@@ -101,8 +101,8 @@
 
 void WebCoreAVFResourceLoader::responseReceived(CachedResource* resource, const ResourceResponse& response)
 {
+    ASSERT(resource);
     ASSERT(resource == m_resource);
-    UNUSED_PARAM(resource);
 
     int status = response.httpStatusCode();
     if (status && (status < 200 || status > 299)) {
@@ -114,7 +114,9 @@
         String uti = UTIFromMIMEType(response.mimeType().createCFString().get()).get();
 
         [contentInfo setContentType:uti];
-        [contentInfo setContentLength:response.expectedContentLength()];
+
+        ParsedContentRange& contentRange = resource->response().contentRange();
+        [contentInfo setContentLength:contentRange.isValid() ? contentRange.instanceLength() : response.expectedContentLength()];
         [contentInfo setByteRangeAccessSupported:YES];
 
         if (![m_avRequest dataRequest]) {
@@ -147,6 +149,7 @@
 
 void WebCoreAVFResourceLoader::fulfillRequestWithResource(CachedResource* resource)
 {
+    ASSERT(resource);
     ASSERT(resource == m_resource);
     AVAssetResourceLoadingDataRequest* dataRequest = [m_avRequest dataRequest];
     if (!dataRequest)
@@ -156,6 +159,11 @@
     if (!data)
         return;
 
+    NSUInteger responseOffset = 0;
+    ParsedContentRange contentRange = resource->response().contentRange();
+    if (contentRange.isValid())
+        responseOffset = static_cast<NSUInteger>(contentRange.firstBytePosition());
+
     // Check for possible unsigned overflow.
     ASSERT([dataRequest currentOffset] >= [dataRequest requestedOffset]);
     ASSERT([dataRequest requestedLength] >= ([dataRequest currentOffset] - [dataRequest requestedOffset]));
@@ -163,11 +171,11 @@
     NSUInteger remainingLength = [dataRequest requestedLength] - static_cast<NSUInteger>([dataRequest currentOffset] - [dataRequest requestedOffset]);
     do {
         // Check to see if there is any data available in the buffer to fulfill the data request.
-        if (data->size() <= [dataRequest currentOffset])
+        if (data->size() <= [dataRequest currentOffset] - responseOffset)
             return;
 
         const char* someData;
-        NSUInteger receivedLength = data->getSomeData(someData, static_cast<unsigned>([dataRequest currentOffset]));
+        NSUInteger receivedLength = data->getSomeData(someData, static_cast<unsigned>([dataRequest currentOffset] - responseOffset));
 
         // Create an NSData with only as much of the received data as necessary to fulfill the request.
         NSUInteger length = MIN(receivedLength, remainingLength);

Modified: trunk/Source/WebCore/platform/network/BlobResourceHandle.cpp (195763 => 195764)


--- trunk/Source/WebCore/platform/network/BlobResourceHandle.cpp	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/network/BlobResourceHandle.cpp	2016-01-28 19:17:17 UTC (rev 195764)
@@ -38,6 +38,7 @@
 #include "FileSystem.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
+#include "ParsedContentRange.h"
 #include "URL.h"
 #include "ResourceError.h"
 #include "ResourceHandleClient.h"
@@ -50,7 +51,6 @@
 namespace WebCore {
 
 static const unsigned bufferSize = 512 * 1024;
-static const long long positionNotSpecified = -1;
 
 static const int httpOK = 200;
 static const int httpPartialContent = 206;
@@ -159,16 +159,6 @@
     : ResourceHandle(0, request, client, false, false)
     , m_blobData(blobData)
     , m_async(async)
-    , m_errorCode(0)
-    , m_aborted(false)
-    , m_rangeOffset(positionNotSpecified)
-    , m_rangeEnd(positionNotSpecified)
-    , m_rangeSuffixLength(positionNotSpecified)
-    , m_totalRemainingSize(0)
-    , m_currentItemReadSize(0)
-    , m_sizeItemCount(0)
-    , m_readItemCount(0)
-    , m_fileOpened(false)
 {
     if (m_async)
         m_asyncStream = std::make_unique<AsyncFileStream>(*this);
@@ -300,6 +290,7 @@
     m_itemLengthList.append(size);
 
     // Count the size.
+    m_totalSize += size;
     m_totalRemainingSize += size;
     m_sizeItemCount++;
 
@@ -312,13 +303,13 @@
     ASSERT(isMainThread());
 
     // Convert from the suffix length to the range.
-    if (m_rangeSuffixLength != positionNotSpecified) {
+    if (m_rangeSuffixLength != kPositionNotSpecified) {
         m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
         m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
     }
 
     // Bail out if the range is not provided.
-    if (m_rangeOffset == positionNotSpecified)
+    if (m_rangeOffset == kPositionNotSpecified)
         return;
 
     // Skip the initial items that are not in the range.
@@ -330,7 +321,7 @@
     m_currentItemReadSize = offset;
 
     // Adjust the total remaining size in order not to go beyond the range.
-    if (m_rangeEnd != positionNotSpecified) {
+    if (m_rangeEnd != kPositionNotSpecified) {
         long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
         if (m_totalRemainingSize > rangeSize)
             m_totalRemainingSize = rangeSize;
@@ -583,10 +574,12 @@
 {
     ASSERT(isMainThread());
 
-    bool isRangeRequest = m_rangeOffset != positionNotSpecified;
+    bool isRangeRequest = m_rangeOffset != kPositionNotSpecified;
     ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String());
     response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
     response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
+    if (isRangeRequest)
+        response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue());
     // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute,
     // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute.
     // Notably, this will affect a name suggested in "File Save As".

Modified: trunk/Source/WebCore/platform/network/BlobResourceHandle.h (195763 => 195764)


--- trunk/Source/WebCore/platform/network/BlobResourceHandle.h	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/network/BlobResourceHandle.h	2016-01-28 19:17:17 UTC (rev 195764)
@@ -90,22 +90,25 @@
     void notifyFail(int errorCode);
     void notifyFinish();
 
+    enum { kPositionNotSpecified = -1 };
+
     RefPtr<BlobData> m_blobData;
     bool m_async;
     std::unique_ptr<AsyncFileStream> m_asyncStream; // For asynchronous loading.
     std::unique_ptr<FileStream> m_stream; // For synchronous loading.
     Vector<char> m_buffer;
     Vector<long long> m_itemLengthList;
-    int m_errorCode;
-    bool m_aborted;
-    long long m_rangeOffset;
-    long long m_rangeEnd;
-    long long m_rangeSuffixLength;
-    long long m_totalRemainingSize;
-    long long m_currentItemReadSize;
-    unsigned m_sizeItemCount;
-    unsigned m_readItemCount;
-    bool m_fileOpened;
+    int m_errorCode { 0 };
+    bool m_aborted { false };
+    long long m_rangeOffset { kPositionNotSpecified };
+    long long m_rangeEnd { kPositionNotSpecified };
+    long long m_rangeSuffixLength { kPositionNotSpecified };
+    long long m_totalSize { 0 };
+    long long m_totalRemainingSize { 0 };
+    long long m_currentItemReadSize { 0 };
+    unsigned m_sizeItemCount { 0 };
+    unsigned m_readItemCount { 0 };
+    bool m_fileOpened { false };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/network/HTTPHeaderNames.in (195763 => 195764)


--- trunk/Source/WebCore/platform/network/HTTPHeaderNames.in	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/network/HTTPHeaderNames.in	2016-01-28 19:17:17 UTC (rev 195764)
@@ -49,6 +49,7 @@
 Content-Security-Policy-Report-Only
 Content-Type
 Content-Transfer-Encoding
+Content-Range
 Cookie
 Cookie2
 Date

Added: trunk/Source/WebCore/platform/network/ParsedContentRange.cpp (0 => 195764)


--- trunk/Source/WebCore/platform/network/ParsedContentRange.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/ParsedContentRange.cpp	2016-01-28 19:17:17 UTC (rev 195764)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 "ParsedContentRange.h"
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+static bool areContentRangeValuesValid(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength)
+{
+    // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>
+    // 14.16 Content-Range
+    // A byte-content-range-spec with a byte-range-resp-spec whose last- byte-pos value is less than its first-byte-pos value,
+    // or whose instance-length value is less than or equal to its last-byte-pos value, is invalid.
+    if (firstBytePosition < 0)
+        return false;
+
+    if (lastBytePosition < firstBytePosition)
+        return false;
+
+    if (instanceLength == ParsedContentRange::UnknownLength)
+        return true;
+
+    return lastBytePosition < instanceLength;
+}
+
+static bool parseContentRange(const String& headerValue, int64_t& firstBytePosition, int64_t& lastBytePosition, int64_t& instanceLength)
+{
+    // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>
+    // 14.16 Content-Range
+    //
+    // Content-Range = "Content-Range" ":" content-range-spec
+    // content-range-spec      = byte-content-range-spec
+    // byte-content-range-spec = bytes-unit SP
+    //                          byte-range-resp-spec "/"
+    //                          ( instance-length | "*" )
+    // byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
+    //                               | "*"
+    // instance-length           = 1*DIGIT
+
+    static const char* prefix = "bytes ";
+    static const size_t prefixLength = 6;
+
+    if (!headerValue.startsWith(prefix))
+        return false;
+
+    size_t byteSeparatorTokenLoc = headerValue.find('-', prefixLength);
+    if (byteSeparatorTokenLoc == notFound)
+        return false;
+
+    size_t instanceLengthSeparatorToken = headerValue.find('/', byteSeparatorTokenLoc + 1);
+    if (instanceLengthSeparatorToken == notFound)
+        return false;
+
+    bool isOk = true;
+    String firstByteString = headerValue.substring(prefixLength, byteSeparatorTokenLoc - prefixLength);
+    if (!firstByteString.isAllSpecialCharacters<isASCIIDigit>())
+        return false;
+
+    firstBytePosition = firstByteString.toInt64Strict(&isOk);
+    if (!isOk)
+        return false;
+
+    String lastByteString = headerValue.substring(byteSeparatorTokenLoc + 1, instanceLengthSeparatorToken - (byteSeparatorTokenLoc + 1));
+    if (!lastByteString.isAllSpecialCharacters<isASCIIDigit>())
+        return false;
+
+    lastBytePosition = lastByteString.toInt64Strict(&isOk);
+    if (!isOk)
+        return false;
+
+    String instanceString = headerValue.substring(instanceLengthSeparatorToken + 1);
+    if (instanceString == "*")
+        instanceLength = ParsedContentRange::UnknownLength;
+    else {
+        if (!instanceString.isAllSpecialCharacters<isASCIIDigit>())
+            return false;
+
+        instanceLength = instanceString.toInt64Strict(&isOk);
+        if (!isOk)
+            return false;
+    }
+
+    return areContentRangeValuesValid(firstBytePosition, lastBytePosition, instanceLength);
+}
+
+ParsedContentRange::ParsedContentRange(const String& headerValue)
+{
+    m_isValid = parseContentRange(headerValue, m_firstBytePosition, m_lastBytePosition, m_instanceLength);
+}
+
+ParsedContentRange::ParsedContentRange(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength)
+    : m_firstBytePosition(firstBytePosition)
+    , m_lastBytePosition(lastBytePosition)
+    , m_instanceLength(instanceLength)
+{
+    m_isValid = areContentRangeValuesValid(m_firstBytePosition, m_lastBytePosition, m_instanceLength);
+}
+
+String ParsedContentRange::headerValue() const
+{
+    if (!m_isValid)
+        return String();
+    if (m_instanceLength == UnknownLength)
+        return String::format("bytes %" PRId64 "-%" PRId64 "/*", m_firstBytePosition, m_lastBytePosition);
+    return String::format("bytes %" PRId64 "-%" PRId64 "/%" PRId64, m_firstBytePosition, m_lastBytePosition, m_instanceLength);
+}
+
+}

Added: trunk/Source/WebCore/platform/network/ParsedContentRange.h (0 => 195764)


--- trunk/Source/WebCore/platform/network/ParsedContentRange.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/ParsedContentRange.h	2016-01-28 19:17:17 UTC (rev 195764)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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. 
+ */
+
+#ifndef ParsedContentRange_h
+#define ParsedContentRange_h
+
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class ParsedContentRange {
+public:
+    WEBCORE_EXPORT explicit ParsedContentRange(const String&);
+    WEBCORE_EXPORT ParsedContentRange() { }
+    WEBCORE_EXPORT ParsedContentRange(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength);
+
+    WEBCORE_EXPORT bool isValid() const { return m_isValid; }
+    int64_t firstBytePosition() const { return m_firstBytePosition; }
+    int64_t lastBytePosition() const { return m_lastBytePosition; }
+    int64_t instanceLength() const { return m_instanceLength; }
+
+    WEBCORE_EXPORT String headerValue() const;
+
+    enum { UnknownLength = std::numeric_limits<int64_t>::max() };
+
+private:
+    template<typename T> static bool isPositive(T);
+
+    bool m_isValid { false };
+    int64_t m_firstBytePosition { 0 };
+    int64_t m_lastBytePosition { 0 };
+    int64_t m_instanceLength { UnknownLength };
+};
+
+}
+
+#endif

Modified: trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp (195763 => 195764)


--- trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp	2016-01-28 19:17:17 UTC (rev 195764)
@@ -30,6 +30,7 @@
 #include "CacheValidation.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
+#include "ParsedContentRange.h"
 #include "ResourceResponse.h"
 #include <wtf/CurrentTime.h>
 #include <wtf/MathExtras.h>
@@ -300,6 +301,10 @@
         m_haveParsedLastModifiedHeader = false;
         break;
 
+    case HTTPHeaderName::ContentRange:
+        m_haveParsedContentRangeHeader = false;
+        break;
+
     default:
         break;
     }
@@ -461,6 +466,27 @@
     return m_lastModified;
 }
 
+static ParsedContentRange parseContentRangeInHeader(const HTTPHeaderMap& headers)
+{
+    String contentRangeValue = headers.get(HTTPHeaderName::ContentRange);
+    if (contentRangeValue.isEmpty())
+        return ParsedContentRange();
+
+    return ParsedContentRange(contentRangeValue);
+}
+
+ParsedContentRange& ResourceResponseBase::contentRange() const
+{
+    lazyInit(CommonFieldsOnly);
+
+    if (!m_haveParsedContentRangeHeader) {
+        m_contentRange = parseContentRangeInHeader(m_httpHeaderFields);
+        m_haveParsedContentRangeHeader = true;
+    }
+
+    return m_contentRange;
+}
+
 bool ResourceResponseBase::isAttachment() const
 {
     lazyInit(AllFields);

Modified: trunk/Source/WebCore/platform/network/ResourceResponseBase.h (195763 => 195764)


--- trunk/Source/WebCore/platform/network/ResourceResponseBase.h	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Source/WebCore/platform/network/ResourceResponseBase.h	2016-01-28 19:17:17 UTC (rev 195764)
@@ -30,6 +30,7 @@
 #include "CacheValidation.h"
 #include "CertificateInfo.h"
 #include "HTTPHeaderMap.h"
+#include "ParsedContentRange.h"
 #include "ResourceLoadTiming.h"
 #include "URL.h"
 
@@ -110,6 +111,7 @@
     WEBCORE_EXPORT Optional<std::chrono::microseconds> age() const;
     WEBCORE_EXPORT Optional<std::chrono::system_clock::time_point> expires() const;
     WEBCORE_EXPORT Optional<std::chrono::system_clock::time_point> lastModified() const;
+    ParsedContentRange& contentRange() const;
 
     // This is primarily for testing support. It is not necessarily accurate in all scenarios.
     enum class Source { Unknown, Network, DiskCache, DiskCacheAfterValidation, MemoryCache, MemoryCacheAfterValidation };
@@ -175,6 +177,7 @@
     mutable Optional<std::chrono::system_clock::time_point> m_date;
     mutable Optional<std::chrono::system_clock::time_point> m_expires;
     mutable Optional<std::chrono::system_clock::time_point> m_lastModified;
+    mutable ParsedContentRange m_contentRange;
     mutable CacheControlDirectives m_cacheControlDirectives;
 
     mutable bool m_haveParsedCacheControlHeader { false };
@@ -182,6 +185,7 @@
     mutable bool m_haveParsedDateHeader { false };
     mutable bool m_haveParsedExpiresHeader { false };
     mutable bool m_haveParsedLastModifiedHeader { false };
+    mutable bool m_haveParsedContentRangeHeader { false };
 
     Source m_source { Source::Unknown };
 };

Modified: trunk/Tools/ChangeLog (195763 => 195764)


--- trunk/Tools/ChangeLog	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Tools/ChangeLog	2016-01-28 19:17:17 UTC (rev 195764)
@@ -1,3 +1,20 @@
+2016-01-12  Jer Noble  <jer.no...@apple.com>
+
+        Custom protocol loading through AVFoundation does not support byte-range requests.
+        https://bugs.webkit.org/show_bug.cgi?id=152919
+        <rdar://problem/23664657>
+
+        Reviewed by Alex Christensen.
+
+        Add tests for new ParsedContntRange class.
+
+        * TestWebKitAPI/PlatformWin.cmake:
+        * TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj:
+        * TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj.filters:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp: Added.
+        (TestWebKitAPI::TEST):
+
 2016-01-28  Konstantin Tokarev  <annu...@yandex.ru>
 
         Use isAnyWindows() instead of isCygwin() || isWindows() in Perl scripts.

Modified: trunk/Tools/TestWebKitAPI/PlatformWin.cmake (195763 => 195764)


--- trunk/Tools/TestWebKitAPI/PlatformWin.cmake	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Tools/TestWebKitAPI/PlatformWin.cmake	2016-01-28 19:17:17 UTC (rev 195764)
@@ -41,6 +41,7 @@
     ${TESTWEBKITAPI_DIR}/Tests/WebCore/CalculationValue.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WebCore/CSSParser.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WebCore/LayoutUnit.cpp
+    ${TESTWEBKITAPI_DIR}/Tests/WebCore/ParsedContentRange.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WebCore/TimeRanges.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WebCore/URL.cpp
 )

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj (195763 => 195764)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj	2016-01-28 19:17:17 UTC (rev 195764)
@@ -295,6 +295,7 @@
     <ClCompile Include="..\Tests\WebCore\CalculationValue.cpp" />
     <ClCompile Include="..\Tests\WebCore\CSSParser.cpp" />
     <ClCompile Include="..\Tests\WebCore\LayoutUnit.cpp" />
+    <ClCompile Include="..\Tests\WebCore\ParsedContentRange.cpp" />
     <ClCompile Include="..\Tests\WebCore\TimeRanges.cpp" />
     <ClCompile Include="..\Tests\WebCore\URL.cpp" />
     <ClCompile Include="..\Tests\WebCore\win\BitmapImage.cpp">

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj.filters (195763 => 195764)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj.filters	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj.filters	2016-01-28 19:17:17 UTC (rev 195764)
@@ -157,6 +157,9 @@
     <ClCompile Include="..\Tests\WebCore\CalculationValue.cpp">
       <Filter>Tests\WebCore</Filter>
     </ClCompile>
+    <ClCompile Include="..\Tests\WebCore\ParsedContentRange.cpp">
+      <Filter>Tests\WebCore</Filter>
+    </ClCompile>
     <ClCompile Include="..\Tests\WebCore\TimeRanges.cpp">
       <Filter>Tests\WebCore</Filter>
     </ClCompile>

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (195763 => 195764)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2016-01-28 19:07:40 UTC (rev 195763)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2016-01-28 19:17:17 UTC (rev 195764)
@@ -313,6 +313,7 @@
 		C540F784152E5A9A00A40C8C /* verboseMarkup.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = C540F783152E5A7800A40C8C /* verboseMarkup.html */; };
 		C54237F116B8957D00E638FC /* PasteboardNotifications_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C54237ED16B8955800E638FC /* PasteboardNotifications_Bundle.cpp */; };
 		C5E1AFFE16B221F1006CC1F2 /* execCopy.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = C5E1AFFD16B22179006CC1F2 /* execCopy.html */; };
+		CD225C081C45A69200140761 /* ParsedContentRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD225C071C45A69200140761 /* ParsedContentRange.cpp */; };
 		CD59F53419E9110D00CF1835 /* file-with-mse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CD59F53219E910AA00CF1835 /* file-with-mse.html */; };
 		CD59F53519E9110D00CF1835 /* test-mse.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CD59F53319E910BC00CF1835 /* test-mse.mp4 */; };
 		CDBFCC451A9FF45300A7B691 /* FullscreenZoomInitialFrame.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDBFCC431A9FF44800A7B691 /* FullscreenZoomInitialFrame.mm */; };
@@ -783,6 +784,7 @@
 		C54237EE16B8955800E638FC /* PasteboardNotifications.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteboardNotifications.mm; sourceTree = "<group>"; };
 		C5E1AFFD16B22179006CC1F2 /* execCopy.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = execCopy.html; sourceTree = "<group>"; };
 		C95501BE19AD2FAF0049BE3E /* Preferences.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Preferences.mm; sourceTree = "<group>"; };
+		CD225C071C45A69200140761 /* ParsedContentRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParsedContentRange.cpp; sourceTree = "<group>"; };
 		CD5393C71757BA9700C07123 /* MD5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MD5.cpp; sourceTree = "<group>"; };
 		CD5393C91757BAC400C07123 /* SHA1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SHA1.cpp; sourceTree = "<group>"; };
 		CD5451E919E41F9D0016936F /* CSSParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSParser.cpp; sourceTree = "<group>"; };
@@ -989,6 +991,7 @@
 				26F6E1EF1ADC749B00DE696B /* DFAMinimizer.cpp */,
 				41973B5A1AF2286A006C7B36 /* FileSystem.cpp */,
 				14464012167A8305000BD218 /* LayoutUnit.cpp */,
+				CD225C071C45A69200140761 /* ParsedContentRange.cpp */,
 				41973B5C1AF22875006C7B36 /* SharedBuffer.cpp */,
 				CDC2C7141797089D00E627FB /* TimeRanges.cpp */,
 				440A1D3814A0103A008A66F2 /* URL.cpp */,
@@ -1835,6 +1838,7 @@
 				1CB9BC381A67482300FE5678 /* WeakPtr.cpp in Sources */,
 				2E7765CD16C4D80A00BA2BB1 /* mainIOS.mm in Sources */,
 				2D8104CC1BEC13E70020DA46 /* FindInPage.mm in Sources */,
+				CD225C081C45A69200140761 /* ParsedContentRange.cpp in Sources */,
 				41973B5D1AF22875006C7B36 /* SharedBuffer.cpp in Sources */,
 				2DD355361BD08378005DF4A7 /* AutoLayoutIntegration.mm in Sources */,
 				7AA6A1521AAC0B31002B2ED3 /* WorkQueue.cpp in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp (0 => 195764)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp	2016-01-28 19:17:17 UTC (rev 195764)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 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 <WebCore/ParsedContentRange.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+TEST(WebCore, ParsedContentRangeFromString)
+{
+    // Basic parsing
+    ASSERT_TRUE(ParsedContentRange("bytes 0-1/2").isValid());
+    ASSERT_TRUE(ParsedContentRange("bytes 0-1/*").isValid());
+    ASSERT_EQ(0, ParsedContentRange("bytes 0-1/2").firstBytePosition());
+    ASSERT_EQ(1, ParsedContentRange("bytes 0-1/2").lastBytePosition());
+    ASSERT_EQ(2, ParsedContentRange("bytes 0-1/2").instanceLength());
+    ASSERT_EQ(ParsedContentRange::UnknownLength, ParsedContentRange("bytes 0-1/*").instanceLength());
+
+    // Whitespace errors
+    ASSERT_FALSE(ParsedContentRange("bytes  0-1/*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0 -1/*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0- 1/*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1 /*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/ *").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/* ").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/ 2").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/2 ").isValid());
+
+    // Non-digit errors
+    ASSERT_FALSE(ParsedContentRange("bytes abcd-1/2").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-abcd/2").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/abcd").isValid());
+
+    // Range requirement errors
+    ASSERT_FALSE(ParsedContentRange("bytes 1-0/2").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-2/1").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 2/0-1").isValid());
+    ASSERT_FALSE(ParsedContentRange("abcd 0/1-2").isValid());
+
+    // Negative value errors
+    ASSERT_FALSE(ParsedContentRange("bytes -0-1/*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes -1/*").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0--0/2").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 0-1/-2").isValid());
+
+    // Edge cases
+    ASSERT_TRUE(ParsedContentRange("bytes 9223372036854775805-9223372036854775806/9223372036854775807").isValid());
+    ASSERT_FALSE(ParsedContentRange("bytes 9223372036854775808-9223372036854775809/9223372036854775810").isValid());
+}
+
+TEST(WebCore, ParsedContentRangeFromValues)
+{
+    ASSERT_TRUE(ParsedContentRange(0, 1, 2).isValid());
+    ASSERT_TRUE(ParsedContentRange(0, 1, ParsedContentRange::UnknownLength).isValid());
+    ASSERT_FALSE(ParsedContentRange().isValid());
+    ASSERT_FALSE(ParsedContentRange(1, 0, 2).isValid());
+    ASSERT_FALSE(ParsedContentRange(0, 2, 1).isValid());
+    ASSERT_FALSE(ParsedContentRange(0, 0, 0).isValid());
+    ASSERT_FALSE(ParsedContentRange(-1, 1, 2).isValid());
+    ASSERT_FALSE(ParsedContentRange(0, -1, 2).isValid());
+    ASSERT_FALSE(ParsedContentRange(0, 1, -2).isValid());
+    ASSERT_FALSE(ParsedContentRange(-2, -1, 2).isValid());
+}
+
+TEST(WebCore, ParsedContentRangeToString)
+{
+    ASSERT_STREQ("bytes 0-1/2", ParsedContentRange(0, 1, 2).headerValue().utf8().data());
+    ASSERT_STREQ("bytes 0-1/*", ParsedContentRange(0, 1, ParsedContentRange::UnknownLength).headerValue().utf8().data());
+    ASSERT_STREQ("", ParsedContentRange().headerValue().utf8().data());
+}
+
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to