Commit bd21e58d authored by peter@chromium.org's avatar peter@chromium.org

Update PushEvent and PushMessageData to match the spec.

The specification defines that PushEvents can be initialized with a
data member that is of type PushMessageDataInit, rather than an actual
PushMessageData object. This allows us to remove the constructor from
the PushMessageData object entirely, aligning with the specification.

https://w3c.github.io/push-api/#pushmessagedata-interface

This CL also implements the following pull requests, which defines the
PushMessageDataInit type to also encapsulate ArrayBuffer(View)?.

https://github.com/w3c/push-api/pull/160

Note that the PushMessageData object has not shipped to the world yet.

R=mvanouwerkerk
BUG=

Review URL: https://codereview.chromium.org/1311853002

git-svn-id: svn://svn.chromium.org/blink/trunk@201129 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 19456e51
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<title>Push API: PushMessageData's text-based constructor and accessor behaviour.</title> <title>Push API: PushMessageData initialization and accessor behaviour.</title>
<script src="../resources/testharness.js"></script> <script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script> <script src="../resources/testharnessreport.js"></script>
<script src="../serviceworker/resources/test-helpers.js"></script> <script src="../serviceworker/resources/test-helpers.js"></script>
</head> </head>
<body> <body>
<script> <script>
// Tests that the PushMessageData object exists, can be constructed and // Tests that the PushMessageData object exists, can be initialized using
// returns the expected values through its text() accessor. // a string or an ArrayBuffer(View) through the PushEvent and returns the
// expected values through its various accessors.
service_worker_test( service_worker_test(
'resources/pushmessagedata-constructor-text.js', 'resources/pushmessagedata-worker.js',
'Exposure and behaviour of the PushMessageData object in a ServiceWorker.'); 'PushMessageData initialization and accessor behaviour in a ServiceWorker.');
</script> </script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -26,7 +26,7 @@ test(function() { ...@@ -26,7 +26,7 @@ test(function() {
test(function() { test(function() {
assert_own_property(self, 'PushMessageData', 'PushMessageData needs to be exposed as a global.'); assert_own_property(self, 'PushMessageData', 'PushMessageData needs to be exposed as a global.');
var pushMessageData = new PushMessageData('SomeData'); var pushMessageData = new PushEvent('PushEvent', { data: 'SomeData' }).data;
assert_inherits(pushMessageData, 'arrayBuffer'); assert_inherits(pushMessageData, 'arrayBuffer');
assert_inherits(pushMessageData, 'blob'); assert_inherits(pushMessageData, 'blob');
assert_inherits(pushMessageData, 'json'); assert_inherits(pushMessageData, 'json');
......
...@@ -11,14 +11,15 @@ test(function() { ...@@ -11,14 +11,15 @@ test(function() {
assert_equals(event.bubbles, false); assert_equals(event.bubbles, false);
assert_inherits(event, 'waitUntil'); assert_inherits(event, 'waitUntil');
var data = new PushMessageData('foo'); const textContents = 'Hello, world!';
var eventWithInit = new PushEvent('PushEvent', var eventWithInit = new PushEvent('PushEvent',
{ cancelable: true, { cancelable: true,
bubbles: true, bubbles: true,
data: data, data: textContents,
}); });
assert_equals(eventWithInit.cancelable, true); assert_equals(eventWithInit.cancelable, true);
assert_equals(eventWithInit.bubbles, true); assert_equals(eventWithInit.bubbles, true);
assert_equals(eventWithInit.data, data); assert_equals(eventWithInit.data.text(), textContents);
}, 'PushEvent is exposed and extends ExtendableEvent.'); }, 'PushEvent is exposed and extends ExtendableEvent.');
importScripts('../../serviceworker/resources/worker-testharness.js');
importScripts('/resources/testharness-helpers.js');
test(function() {
var data = new PushMessageData('Hello, world!');
assert_equals(data.text(), 'Hello, world!');
}, 'PushMessageData can be constructed with a string parameter.');
test(function() {
var event = new PushEvent('PushEvent');
assert_equals(event.data.arrayBuffer().byteLength, 0);
assert_equals(event.data.blob().size, 0);
assert_equals(event.data.blob().type, '');
// PushMessageData.json() is specified to be identical to JSON.parse() with
// the message's data as the argument. JSON.parse('') throws an exception,
// so verify that calling json() without a body throws the same exception.
try {
event.data.json();
} catch (eventDataException) {
try {
JSON.parse('');
} catch (jsonParseException) {
assert_equals(eventDataException.name, jsonParseException.name);
assert_equals(eventDataException.message, jsonParseException.message);
}
}
assert_equals(event.data.text().length, 0);
}, 'PushMessageData is empty by default, given a PushEvent constructor.');
test(function() {
var event = new PushEvent('PushEvent', {
data: new PushMessageData('FOOBAR')
});
var arrayBuffer = event.data.arrayBuffer();
assert_equals(arrayBuffer.byteLength, 6);
var int8Array = new Int8Array(arrayBuffer);
assert_equals(int8Array[0], 70); // F
assert_equals(int8Array[3], 66); // B
}, 'Reading an ArrayBuffer from PushMessageData through the PushEvent constructor.');
async_test(function(test) {
var event = new PushEvent('PushEvent', {
data: new PushMessageData('FOOBAR')
});
var blob = event.data.blob();
assert_equals(blob.size, 6);
assert_equals(blob.type, '');
var reader = new FileReader();
reader.addEventListener('load', function() {
assert_equals(reader.result, 'FOOBAR');
test.done();
});
reader.readAsText(blob);
}, 'Reading a Blob from PushMessageData through the PushEvent constructor.')
test(function() {
var event = new PushEvent('PushEvent', {
data: new PushMessageData('[5, 6, 7]')
});
var array = event.data.json();
assert_equals(array.length, 3);
assert_equals(array[1], 6);
event = new PushEvent('PushEvent', {
data: new PushMessageData('{ "foo": { "bar": 42 } }')
});
assert_equals(event.data.json().foo.bar, 42);
}, 'Reading JSON from PushMessageData through the PushEvent constructor.');
test(function() {
var event = new PushEvent('PushEvent', {
data: new PushMessageData('Hello, world!')
});
assert_equals(event.data.text(), 'Hello, world!');
}, 'Reading text from PushMessageData through the PushEvent constructor.');
importScripts('../../serviceworker/resources/worker-testharness.js');
importScripts('/resources/testharness-helpers.js');
function createPushMessageData(data)
{
// The PushMessageData object does not expose a constructor, but we can get an object
// initialized with our data by constructing a PushEvent.
return new PushEvent('PushEvent', { data: data }).data;
}
test(function() {
const textContents = 'Hello, world!';
var runTest = pushEvent => {
assert_not_equals(pushEvent.data, null);
assert_equals(pushEvent.data.text(), textContents);
};
// JavaScript strings are UTF-16, whereas binary data for push message data will be
// encoded as UTF-8. Convert accordingly.
var bufferView = new TextEncoder('utf-8').encode(textContents);
runTest(new PushEvent('PushEvent', { data: bufferView }));
runTest(new PushEvent('PushEvent', { data: bufferView.buffer }));
runTest(new PushEvent('PushEvent', { data: textContents }));
}, 'PushEvent can be initialized from ArrayBuffer, ArrayBufferView and USVStrings.');
test(function() {
var pushMessageData = createPushMessageData();
assert_equals(pushMessageData.arrayBuffer().byteLength, 0);
assert_equals(pushMessageData.blob().size, 0);
assert_equals(pushMessageData.blob().type, '');
// PushMessageData.json() is specified to be identical to JSON.parse() with
// the message's data as the argument. JSON.parse('') throws an exception,
// so verify that calling json() without a body throws the same exception.
try {
pushMessageData.json();
assert_unreached('Expected an exception to be thrown.');
} catch (eventDataException) {
try {
JSON.parse('');
assert_unreached('Expected an exception to be thrown.');
} catch (jsonParseException) {
assert_equals(eventDataException.name, jsonParseException.name);
assert_equals(eventDataException.message, jsonParseException.message);
}
}
assert_equals(pushMessageData.text().length, 0);
}, 'PushMessageData is empty by default.');
test(function() {
const binaryContents = [1, 2, 3];
const textContents = 'FOOBAR';
var pushMessageDataBinary = createPushMessageData(new Uint8Array(binaryContents)),
pushMessageDataString = createPushMessageData(textContents);
var binaryBuffer = pushMessageDataBinary.arrayBuffer(),
binaryBufferView = new Uint8Array(binaryBuffer);
assert_equals(binaryBuffer.byteLength, binaryContents.length);
assert_array_equals(binaryBufferView, binaryContents);
var stringBuffer = pushMessageDataString.arrayBuffer(),
stringBufferView = new Int8Array(stringBuffer);
assert_equals(stringBufferView.length, textContents.length);
assert_equals(stringBufferView[0], 70 /* F */);
assert_equals(stringBufferView[3], 66 /* B */);
}, 'PushMessageData handling of ArrayBuffer content.');
async_test(function(test) {
const textContents = 'Hello, world!';
var pushMessageData = createPushMessageData(textContents);
var blob = pushMessageData.blob(),
reader = new FileReader();
assert_equals(blob.size, textContents.length);
assert_equals(blob.type, '');
reader.addEventListener('load', () => {
assert_equals(reader.result, textContents);
test.done();
});
reader.readAsText(blob);
}, 'PushMessageData handling of Blob content.')
test(function() {
var pushMessageDataArray = createPushMessageData('[5, 6, 7]'),
pushMessageDataObject = createPushMessageData('{ "foo": { "bar": 42 } }'),
pushMessageDataString = createPushMessageData('"foobar"');
var array = pushMessageDataArray.json();
assert_equals(array.length, 3);
assert_equals(array[1], 6);
var object = pushMessageDataObject.json();
assert_equals(object.foo.bar, 42);
var string = pushMessageDataString.json();
assert_equals(string, 'foobar');
}, 'PushMessageData handling of valid JSON content.');
test(function() {
// Note that we do not care about the exception code - it's pass through.
assert_throws(null, () => createPushMessageData('\\o/').json());
}, 'PushMessageData handling of invalid JSON content.');
test(function() {
assert_throws(null, () => new PushMessageData());
assert_throws(null, () => new PushMessageData('Hello, world!'));
assert_throws(null, () => new PushMessageData(new ArrayBuffer(8)));
}, 'PushMessageData should not be constructable.');
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "config.h" #include "config.h"
#include "modules/push_messaging/PushEvent.h" #include "modules/push_messaging/PushEvent.h"
#include "modules/push_messaging/PushEventInit.h"
namespace blink { namespace blink {
PushEvent::PushEvent() PushEvent::PushEvent()
...@@ -21,7 +23,7 @@ PushEvent::PushEvent(const AtomicString& type, const PushEventInit& initializer) ...@@ -21,7 +23,7 @@ PushEvent::PushEvent(const AtomicString& type, const PushEventInit& initializer)
: ExtendableEvent(type, initializer) : ExtendableEvent(type, initializer)
{ {
if (initializer.hasData()) if (initializer.hasData())
m_data = initializer.data(); m_data = PushMessageData::create(initializer.data());
} }
PushEvent::~PushEvent() PushEvent::~PushEvent()
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include "modules/EventModules.h" #include "modules/EventModules.h"
#include "modules/ModulesExport.h" #include "modules/ModulesExport.h"
#include "modules/push_messaging/PushEventInit.h"
#include "modules/push_messaging/PushMessageData.h" #include "modules/push_messaging/PushMessageData.h"
#include "modules/serviceworkers/ExtendableEvent.h" #include "modules/serviceworkers/ExtendableEvent.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
...@@ -16,6 +15,8 @@ ...@@ -16,6 +15,8 @@
namespace blink { namespace blink {
class PushEventInit;
class MODULES_EXPORT PushEvent final : public ExtendableEvent { class MODULES_EXPORT PushEvent final : public ExtendableEvent {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
public: public:
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
// https://w3c.github.io/push-api/#idl-def-PushEvent // https://w3c.github.io/push-api/#idl-def-PushEvent
// TODO(peter): Use BufferSource when union types can refer to other union types.
typedef (ArrayBuffer or ArrayBufferView or USVString) PushMessageDataInit;
dictionary PushEventInit : ExtendableEventInit { dictionary PushEventInit : ExtendableEventInit {
PushMessageData data; PushMessageDataInit data;
}; };
...@@ -8,20 +8,46 @@ ...@@ -8,20 +8,46 @@
#include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptState.h" #include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/V8Binding.h" #include "bindings/core/v8/V8Binding.h"
#include "bindings/modules/v8/UnionTypesModules.h"
#include "core/dom/DOMArrayBuffer.h" #include "core/dom/DOMArrayBuffer.h"
#include "core/fileapi/Blob.h" #include "core/fileapi/Blob.h"
#include "platform/blob/BlobData.h" #include "platform/blob/BlobData.h"
#include "wtf/text/TextEncoding.h"
#include <v8.h> #include <v8.h>
namespace blink { namespace blink {
PushMessageData* PushMessageData::create(const String& messageString)
{
return PushMessageData::create(ArrayBufferOrArrayBufferViewOrUSVString::fromUSVString(messageString));
}
PushMessageData* PushMessageData::create(const ArrayBufferOrArrayBufferViewOrUSVString& messageData)
{
if (messageData.isArrayBuffer() || messageData.isArrayBufferView()) {
RefPtr<DOMArrayBuffer> buffer = messageData.isArrayBufferView()
? messageData.getAsArrayBufferView()->buffer()
: messageData.getAsArrayBuffer();
return new PushMessageData(static_cast<const char*>(buffer->data()), buffer->byteLength());
}
if (messageData.isUSVString()) {
CString encodedString = UTF8Encoding().normalizeAndEncode(messageData.getAsUSVString(), WTF::EntitiesForUnencodables);
return new PushMessageData(encodedString.data(), encodedString.length());
}
ASSERT(messageData.isNull());
return new PushMessageData();
}
PushMessageData::PushMessageData() PushMessageData::PushMessageData()
{ {
} }
PushMessageData::PushMessageData(const String& messageData) PushMessageData::PushMessageData(const char* data, unsigned bytesSize)
: m_messageData(messageData)
{ {
m_data.append(data, bytesSize);
} }
PushMessageData::~PushMessageData() PushMessageData::~PushMessageData()
...@@ -30,19 +56,19 @@ PushMessageData::~PushMessageData() ...@@ -30,19 +56,19 @@ PushMessageData::~PushMessageData()
PassRefPtr<DOMArrayBuffer> PushMessageData::arrayBuffer() const PassRefPtr<DOMArrayBuffer> PushMessageData::arrayBuffer() const
{ {
return DOMArrayBuffer::create(m_messageData.characters8(), m_messageData.length()); return DOMArrayBuffer::create(m_data.data(), m_data.size());
} }
Blob* PushMessageData::blob() const Blob* PushMessageData::blob() const
{ {
OwnPtr<BlobData> blobData = BlobData::create(); OwnPtr<BlobData> blobData = BlobData::create();
blobData->appendText(m_messageData, false); blobData->appendBytes(m_data.data(), m_data.size());
// Note that the content type of the Blob object is deliberately not being // Note that the content type of the Blob object is deliberately not being
// provided, following the specification. // provided, following the specification.
const long long blobSize = blobData->length(); const long long byteLength = blobData->length();
return Blob::create(BlobDataHandle::create(blobData.release(), blobSize)); return Blob::create(BlobDataHandle::create(blobData.release(), byteLength));
} }
ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exceptionState) const ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exceptionState) const
...@@ -50,7 +76,7 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce ...@@ -50,7 +76,7 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce
v8::Isolate* isolate = scriptState->isolate(); v8::Isolate* isolate = scriptState->isolate();
ScriptState::Scope scope(scriptState); ScriptState::Scope scope(scriptState);
v8::Local<v8::String> dataString = v8String(isolate, m_messageData); v8::Local<v8::String> dataString = v8String(isolate, text());
v8::TryCatch block; v8::TryCatch block;
v8::Local<v8::Value> parsed; v8::Local<v8::Value> parsed;
...@@ -62,9 +88,9 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce ...@@ -62,9 +88,9 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce
return ScriptValue(scriptState, parsed); return ScriptValue(scriptState, parsed);
} }
const String& PushMessageData::text() const String PushMessageData::text() const
{ {
return m_messageData; return UTF8Encoding().decode(m_data.data(), m_data.size());
} }
DEFINE_TRACE(PushMessageData) DEFINE_TRACE(PushMessageData)
......
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
#include "bindings/core/v8/ScriptWrappable.h" #include "bindings/core/v8/ScriptWrappable.h"
#include "modules/ModulesExport.h" #include "modules/ModulesExport.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
#include "wtf/Vector.h"
#include "wtf/text/WTFString.h" #include "wtf/text/WTFString.h"
namespace blink { namespace blink {
class ArrayBufferOrArrayBufferViewOrUSVString;
class Blob; class Blob;
class DOMArrayBuffer; class DOMArrayBuffer;
class ExceptionState; class ExceptionState;
...@@ -26,26 +28,23 @@ public: ...@@ -26,26 +28,23 @@ public:
{ {
return new PushMessageData(); return new PushMessageData();
} }
static PushMessageData* create(const String& data);
static PushMessageData* create(const String& messageData) static PushMessageData* create(const ArrayBufferOrArrayBufferViewOrUSVString& data);
{
return new PushMessageData(messageData);
}
virtual ~PushMessageData(); virtual ~PushMessageData();
PassRefPtr<DOMArrayBuffer> arrayBuffer() const; PassRefPtr<DOMArrayBuffer> arrayBuffer() const;
Blob* blob() const; Blob* blob() const;
ScriptValue json(ScriptState*, ExceptionState&) const; ScriptValue json(ScriptState*, ExceptionState&) const;
const String& text() const; String text() const;
DECLARE_TRACE(); DECLARE_TRACE();
private: private:
PushMessageData(); PushMessageData();
explicit PushMessageData(const String& messageData); PushMessageData(const char* data, unsigned bytesSize);
String m_messageData; Vector<char> m_data;
}; };
} // namespace blink } // namespace blink
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
typedef object JSON; typedef object JSON;
[ [
Constructor(USVString message),
Exposed=ServiceWorker, Exposed=ServiceWorker,
GarbageCollected, GarbageCollected,
RuntimeEnabled=PushMessagingData, RuntimeEnabled=PushMessagingData,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment