Title: [203494] trunk
Revision
203494
Author
[email protected]
Date
2016-07-20 23:51:05 -0700 (Wed, 20 Jul 2016)

Log Message

[XHR] Cache response JS object in case of arraybuffer and blob response types
https://bugs.webkit.org/show_bug.cgi?id=128903

Patch by Youenn Fablet <[email protected]> on 2016-07-20
Reviewed by Alex Christensen.

Source/WebCore:

Covered by existing and modified tests.

Making response getter a JS builtin that caches response in @response private slot.
Handling invalidation of cached response with @responseCacheIsValid new private method.
Handling creation of cached response with @retrieveResponse new private method which reuses most of
JSXMLHttpRequest::response previous code.

Caching of responses is activated whenever load ended without any error for blob and arraybuffer response types.

Caching of response for document is also activated in case the response getter is used but not if responseXML getter is used.

* CMakeLists.txt: Adding XMLHttpRequest.js.
* DerivedSources.make: Ditto.
* bindings/js/JSXMLHttpRequestCustom.cpp:
(WebCore::JSXMLHttpRequest::retrieveResponse): Implements creation of to-be-cached response.
(WebCore::JSXMLHttpRequest::response): Deleted.
* bindings/js/WebCoreBuiltinNames.h: Adding new private names.
* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::didCacheResponse): Renamed from didCacheResponseJSON as all response types are now cached.
(WebCore::XMLHttpRequest::didCacheResponseJSON): Deleted.
* xml/XMLHttpRequest.h:
* xml/XMLHttpRequest.idl:

LayoutTests:

* http/tests/xmlhttprequest/onabort-response-getters-expected.txt:
* http/tests/xmlhttprequest/onabort-response-getters.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (203493 => 203494)


--- trunk/LayoutTests/ChangeLog	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/LayoutTests/ChangeLog	2016-07-21 06:51:05 UTC (rev 203494)
@@ -1,3 +1,13 @@
+2016-07-20  Youenn Fablet  <[email protected]>
+
+        [XHR] Cache response JS object in case of arraybuffer and blob response types
+        https://bugs.webkit.org/show_bug.cgi?id=128903
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/xmlhttprequest/onabort-response-getters-expected.txt:
+        * http/tests/xmlhttprequest/onabort-response-getters.html:
+
 2016-07-19  Filip Pizlo  <[email protected]>
 
         Switching on symbols should be fast

Modified: trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters-expected.txt (203493 => 203494)


--- trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters-expected.txt	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters-expected.txt	2016-07-21 06:51:05 UTC (rev 203494)
@@ -1,6 +1,12 @@
 
 PASS getting arraybuffer response within abort event callback 
+PASS getting arraybuffer response within abort event callback (aborting in loadend) 
 PASS getting blob response within abort event callback 
+PASS getting blob response within abort event callback (aborting in loadend) 
 PASS getting json response within abort event callback 
+PASS getting json response within abort event callback (aborting in loadend) 
 PASS getting document response within abort event callback 
+PASS getting document response within abort event callback (aborting in loadend) 
+PASS getting text response within abort event callback 
+PASS getting text response within abort event callback (aborting in loadend) 
 

Modified: trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters.html (203493 => 203494)


--- trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters.html	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/LayoutTests/http/tests/xmlhttprequest/onabort-response-getters.html	2016-07-21 06:51:05 UTC (rev 203494)
@@ -7,6 +7,14 @@
   <body>
     <div id="log"></div>
     <script>
+    function checkCachedResponse(client) {
+        assert_true(client.response === client.response);
+        if (client.responseType == "text")
+            assert_true(client.responseText === client.response);
+        if (client.responseType == "document")
+            assert_true(client.responseXML === client.response);
+    }
+
     function runTest(name, fileName, mimeType, setupClient, checkResponse) {
       var test = async_test(name)
       test.step(function() {
@@ -28,6 +36,7 @@
             if (client.readyState == 4) {
                 checkResponse(test, client)
                 client.isResponseChecked = true
+                checkCachedResponse(client);
             }
         })
 
@@ -38,10 +47,29 @@
         client._onloadend_ = test.step_func(function () {
             assert_true(client.hasAborted, "xhr should have aborted")
             assert_true(client.isResponseChecked, "xhr response should have been checked")
+            checkCachedResponse(client);
             test.done()
         })
         client.send(null)
       })
+
+      var test2 = async_test(name + " (aborting in loadend)")
+      test2.step(function() {
+        var client = new XMLHttpRequest()
+        var url = "" + fileName + "&waitFor=1&mimeType=" + mimeType
+        client.open("GET", url, true)
+        setupClient(test2, client)
+
+        client._onloadend_ = test2.step_func(function () {
+            assert_true(client.response != null);
+            checkCachedResponse(client);
+            client.abort();
+            checkResponse(test2, client);
+            checkCachedResponse(client);
+            test2.done();
+        })
+        client.send(null)
+      })
     }
 
     runTest("getting arraybuffer response within abort event callback",
@@ -68,6 +96,12 @@
         function(test, client) {assert_true(client.response == null, "document response must be empty")}
     )
 
+    runTest("getting text response within abort event callback",
+        "resources/test.json","text/plain",
+        function(test, client) {client.responseType = "text"},
+        function(test, client) {assert_true(client.response == "", "text response must be an empty string")}
+    )
+
     </script>
   </body>
 </html>

Modified: trunk/Source/WebCore/CMakeLists.txt (203493 => 203494)


--- trunk/Source/WebCore/CMakeLists.txt	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/CMakeLists.txt	2016-07-21 06:51:05 UTC (rev 203494)
@@ -3726,6 +3726,7 @@
     ${WEBCORE_DIR}/Modules/streams/StreamInternals.js
     ${WEBCORE_DIR}/Modules/streams/WritableStream.js
     ${WEBCORE_DIR}/Modules/streams/WritableStreamInternals.js
+    ${WEBCORE_DIR}/xml/XMLHttpRequest.js
 )
 
 set(BUILTINS_GENERATOR_SCRIPTS

Modified: trunk/Source/WebCore/ChangeLog (203493 => 203494)


--- trunk/Source/WebCore/ChangeLog	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/ChangeLog	2016-07-21 06:51:05 UTC (rev 203494)
@@ -1,5 +1,35 @@
 2016-07-20  Youenn Fablet  <[email protected]>
 
+        [XHR] Cache response JS object in case of arraybuffer and blob response types
+        https://bugs.webkit.org/show_bug.cgi?id=128903
+
+        Reviewed by Alex Christensen.
+
+        Covered by existing and modified tests.
+
+        Making response getter a JS builtin that caches response in @response private slot.
+        Handling invalidation of cached response with @responseCacheIsValid new private method.
+        Handling creation of cached response with @retrieveResponse new private method which reuses most of
+        JSXMLHttpRequest::response previous code.
+
+        Caching of responses is activated whenever load ended without any error for blob and arraybuffer response types.
+
+        Caching of response for document is also activated in case the response getter is used but not if responseXML getter is used.
+
+        * CMakeLists.txt: Adding XMLHttpRequest.js.
+        * DerivedSources.make: Ditto.
+        * bindings/js/JSXMLHttpRequestCustom.cpp:
+        (WebCore::JSXMLHttpRequest::retrieveResponse): Implements creation of to-be-cached response.
+        (WebCore::JSXMLHttpRequest::response): Deleted.
+        * bindings/js/WebCoreBuiltinNames.h: Adding new private names.
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::didCacheResponse): Renamed from didCacheResponseJSON as all response types are now cached.
+        (WebCore::XMLHttpRequest::didCacheResponseJSON): Deleted.
+        * xml/XMLHttpRequest.h:
+        * xml/XMLHttpRequest.idl:
+
+2016-07-20  Youenn Fablet  <[email protected]>
+
         Remove crossOriginRequestPolicy from ThreadableLoaderOptions
         https://bugs.webkit.org/show_bug.cgi?id=159417
 

Modified: trunk/Source/WebCore/DerivedSources.make (203493 => 203494)


--- trunk/Source/WebCore/DerivedSources.make	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/DerivedSources.make	2016-07-21 06:51:05 UTC (rev 203494)
@@ -1299,6 +1299,7 @@
     $(WebCore)/Modules/streams/StreamInternals.js \
     $(WebCore)/Modules/streams/WritableStream.js \
     $(WebCore)/Modules/streams/WritableStreamInternals.js \
+    $(WebCore)/xml/XMLHttpRequest.js \
 #
 
 BUILTINS_GENERATOR_SCRIPTS = \

Modified: trunk/Source/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp (203493 => 203494)


--- trunk/Source/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp	2016-07-21 06:51:05 UTC (rev 203494)
@@ -63,12 +63,6 @@
 
     if (Document* responseDocument = wrapped().optionalResponseXML())
         visitor.addOpaqueRoot(responseDocument);
-
-    if (ArrayBuffer* responseArrayBuffer = wrapped().optionalResponseArrayBuffer())
-        visitor.addOpaqueRoot(responseArrayBuffer);
-
-    if (Blob* responseBlob = wrapped().optionalResponseBlob())
-        visitor.addOpaqueRoot(responseBlob);
 }
 
 class SendFunctor {
@@ -151,12 +145,8 @@
     return jsOwnedStringOrNull(&state, text);
 }
 
-JSValue JSXMLHttpRequest::response(ExecState& state) const
+JSValue JSXMLHttpRequest::retrieveResponse(ExecState& state)
 {
-    // FIXME: Use CachedAttribute for other types than JSON as well.
-    if (m_response && wrapped().responseCacheIsValid())
-        return m_response.get();
-
     auto type = wrapped().responseType();
 
     switch (type) {
@@ -170,42 +160,36 @@
     if (!wrapped().doneWithoutErrors())
         return jsNull();
 
+    JSValue value;
     switch (type) {
     case XMLHttpRequest::ResponseType::EmptyString:
     case XMLHttpRequest::ResponseType::Text:
         ASSERT_NOT_REACHED();
-        break;
+        return jsUndefined();
 
     case XMLHttpRequest::ResponseType::Json:
-        {
-            JSValue value = JSONParse(&state, wrapped().responseTextIgnoringResponseType());
-            if (!value)
-                value = jsNull();
-            m_response.set(state.vm(), this, value);
-            wrapped().didCacheResponseJSON();
-            return value;
-        }
+        value = JSONParse(&state, wrapped().responseTextIgnoringResponseType());
+        if (!value)
+            value = jsNull();
+        break;
 
-    case XMLHttpRequest::ResponseType::Document:
-        {
-            ExceptionCode ec = 0;
-            Document* document = wrapped().responseXML(ec);
-            if (ec) {
-                setDOMException(&state, ec);
-                return jsUndefined();
-            }
-            return toJS(&state, globalObject(), document);
-        }
-
+    case XMLHttpRequest::ResponseType::Document: {
+        ExceptionCode ec = 0;
+        auto document = wrapped().responseXML(ec);
+        ASSERT(!ec);
+        value = toJS(&state, globalObject(), document);
+        break;
+    }
     case XMLHttpRequest::ResponseType::Blob:
-        return toJS(&state, globalObject(), wrapped().responseBlob());
+        value = toJSNewlyCreated(&state, globalObject(), wrapped().createResponseBlob());
+        break;
 
     case XMLHttpRequest::ResponseType::Arraybuffer:
-        return toJS(&state, globalObject(), wrapped().responseArrayBuffer());
+        value = toJS(&state, globalObject(), wrapped().createResponseArrayBuffer());
+        break;
     }
-
-    ASSERT_NOT_REACHED();
-    return jsUndefined();
+    wrapped().didCacheResponse();
+    return value;
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/js/WebCoreBuiltinNames.h (203493 => 203494)


--- trunk/Source/WebCore/bindings/js/WebCoreBuiltinNames.h	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/bindings/js/WebCoreBuiltinNames.h	2016-07-21 06:51:05 UTC (rev 203494)
@@ -62,6 +62,9 @@
     macro(readRequests) \
     macro(readyPromiseCapability) \
     macro(removeTrack) \
+    macro(responseCacheIsValid) \
+    macro(retrieveResponse) \
+    macro(response) \
     macro(setStatus) \
     macro(state) \
     macro(started) \
@@ -85,6 +88,7 @@
     macro(ReadableStreamController) \
     macro(RTCIceCandidate) \
     macro(RTCSessionDescription) \
+    macro(XMLHttpRequest)
 
 class WebCoreBuiltinNames {
 public:

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.cpp (203493 => 203494)


--- trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2016-07-21 06:51:05 UTC (rev 203494)
@@ -171,9 +171,8 @@
     return responseTextIgnoringResponseType();
 }
 
-void XMLHttpRequest::didCacheResponseJSON()
+void XMLHttpRequest::didCacheResponse()
 {
-    ASSERT(m_responseType == ResponseType::Json);
     ASSERT(doneWithoutErrors());
     m_responseCacheIsValid = true;
     m_responseBuilder.clear();
@@ -218,42 +217,30 @@
     return m_responseDocument.get();
 }
 
-Blob* XMLHttpRequest::responseBlob()
+Ref<Blob> XMLHttpRequest::createResponseBlob()
 {
     ASSERT(m_responseType == ResponseType::Blob);
     ASSERT(doneWithoutErrors());
 
-    if (!m_responseBlob) {
-        if (m_binaryResponseBuilder) {
-            // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
-            Vector<uint8_t> data;
-            data.append(m_binaryResponseBuilder->data(), m_binaryResponseBuilder->size());
-            String normalizedContentType = Blob::normalizedContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
-            m_responseBlob = Blob::create(WTFMove(data), normalizedContentType);
-            m_binaryResponseBuilder = nullptr;
-        } else {
-            // If we errored out or got no data, we still return a blob, just an empty one.
-            m_responseBlob = Blob::create();
-        }
-    }
+    if (!m_binaryResponseBuilder)
+        return Blob::create();
 
-    return m_responseBlob.get();
+    // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
+    Vector<uint8_t> data;
+    data.append(m_binaryResponseBuilder->data(), m_binaryResponseBuilder->size());
+    m_binaryResponseBuilder = nullptr;
+    String normalizedContentType = Blob::normalizedContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
+    return Blob::create(WTFMove(data), normalizedContentType);
 }
 
-ArrayBuffer* XMLHttpRequest::responseArrayBuffer()
+RefPtr<ArrayBuffer> XMLHttpRequest::createResponseArrayBuffer()
 {
     ASSERT(m_responseType == ResponseType::Arraybuffer);
     ASSERT(doneWithoutErrors());
 
-    if (!m_responseArrayBuffer) {
-        if (m_binaryResponseBuilder)
-            m_responseArrayBuffer = m_binaryResponseBuilder->createArrayBuffer();
-        else
-            m_responseArrayBuffer = ArrayBuffer::create(nullptr, 0);
-        m_binaryResponseBuilder = nullptr;
-    }
-
-    return m_responseArrayBuffer.get();
+    auto result = m_binaryResponseBuilder ? m_binaryResponseBuilder->createArrayBuffer() : ArrayBuffer::create(nullptr, 0);
+    m_binaryResponseBuilder = nullptr;
+    return result;
 }
 
 void XMLHttpRequest::setTimeout(unsigned timeout, ExceptionCode& ec)
@@ -819,9 +806,7 @@
     m_responseEncoding = String();
     m_createdDocument = false;
     m_responseDocument = nullptr;
-    m_responseBlob = nullptr;
     m_binaryResponseBuilder = nullptr;
-    m_responseArrayBuffer = nullptr;
     m_responseCacheIsValid = false;
 }
 

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.h (203493 => 203494)


--- trunk/Source/WebCore/xml/XMLHttpRequest.h	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.h	2016-07-21 06:51:05 UTC (rev 203494)
@@ -88,15 +88,18 @@
     String responseText(ExceptionCode&);
     String responseTextIgnoringResponseType() const { return m_responseBuilder.toStringPreserveCapacity(); }
     String responseMIMEType() const;
+
+    Document* optionalResponseXML() const { return m_responseDocument.get(); }
     Document* responseXML(ExceptionCode&);
-    Document* optionalResponseXML() const { return m_responseDocument.get(); }
-    Blob* responseBlob();
-    Blob* optionalResponseBlob() const { return m_responseBlob.get(); }
+
+    Ref<Blob> createResponseBlob();
+    RefPtr<JSC::ArrayBuffer> createResponseArrayBuffer();
+
     unsigned timeout() const { return m_timeoutMilliseconds; }
     void setTimeout(unsigned timeout, ExceptionCode&);
 
     bool responseCacheIsValid() const { return m_responseCacheIsValid; }
-    void didCacheResponseJSON();
+    void didCacheResponse();
 
     // Expose HTTP validation methods for other untrusted requests.
     static bool isAllowedHTTPMethod(const String&);
@@ -109,10 +112,6 @@
 
     String responseURL() const;
 
-    // response attribute has custom getter.
-    JSC::ArrayBuffer* responseArrayBuffer();
-    JSC::ArrayBuffer* optionalResponseArrayBuffer() const { return m_responseArrayBuffer.get(); }
-
     void setLastSendLineAndColumnNumber(unsigned lineNumber, unsigned columnNumber);
     void setLastSendURL(const String& url) { m_lastSendURL = url; }
 
@@ -187,7 +186,6 @@
     String m_mimeTypeOverride;
     bool m_async { true };
     bool m_includeCredentials { false };
-    RefPtr<Blob> m_responseBlob;
 
     RefPtr<ThreadableLoader> m_loader;
     State m_state { UNSENT };
@@ -201,9 +199,8 @@
     StringBuilder m_responseBuilder;
     bool m_createdDocument { false };
     RefPtr<Document> m_responseDocument;
-    
+
     RefPtr<SharedBuffer> m_binaryResponseBuilder;
-    RefPtr<JSC::ArrayBuffer> m_responseArrayBuffer;
 
     bool m_error { false };
 

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.idl (203493 => 203494)


--- trunk/Source/WebCore/xml/XMLHttpRequest.idl	2016-07-21 06:41:43 UTC (rev 203493)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.idl	2016-07-21 06:51:05 UTC (rev 203494)
@@ -44,6 +44,8 @@
     JSGenerateToNativeObject,
     JSGenerateToJSObject,
     ExportMacro=WEBCORE_EXPORT,
+    PublicIdentifier,
+    PrivateIdentifier,
 ] interface XMLHttpRequest : XMLHttpRequestEventTarget {
     attribute EventHandler onreadystatechange;
 
@@ -78,12 +80,15 @@
     [GetterRaisesException] readonly attribute Document responseXML;
 
     [SetterRaisesException] attribute XMLHttpRequestResponseType responseType;
-    [GetterRaisesException, CachedAttribute, CustomGetter] readonly attribute Object response;
+    [JSBuiltin] readonly attribute Object response;
 
     readonly attribute unsigned short status;
     readonly attribute DOMString statusText;
     readonly attribute DOMString responseURL;
 
+    [PrivateIdentifier] boolean responseCacheIsValid();
+    [PrivateIdentifier, Custom] any retrieveResponse();
+
     // Extension
     [RaisesException] void overrideMimeType(DOMString override);
 };

Added: trunk/Source/WebCore/xml/XMLHttpRequest.js (0 => 203494)


--- trunk/Source/WebCore/xml/XMLHttpRequest.js	                        (rev 0)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.js	2016-07-21 06:51:05 UTC (rev 203494)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Apple Inc.
+ *
+ * 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. ``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
+ * 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.
+ */
+
+function response()
+{
+    "use strict";
+
+    // FIXME: Add a helper routine for that kind of checks.
+    if (!(this instanceof @XMLHttpRequest))
+        throw new @TypeError("The XMLHttpRequest.response getter can only be used on instances of XMLHttpRequest");
+
+    if (@XMLHttpRequest.prototype.@responseCacheIsValid.@call(this))
+        return this.@response;
+
+    this.@response = @XMLHttpRequest.prototype.@retrieveResponse.@call(this);
+    return this.@response;
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to