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>
<html>
<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/testharnessreport.js"></script>
<script src="../serviceworker/resources/test-helpers.js"></script>
</head>
<body>
<script>
// Tests that the PushMessageData object exists, can be constructed and
// returns the expected values through its text() accessor.
// Tests that the PushMessageData object exists, can be initialized using
// a string or an ArrayBuffer(View) through the PushEvent and returns the
// expected values through its various accessors.
service_worker_test(
'resources/pushmessagedata-constructor-text.js',
'Exposure and behaviour of the PushMessageData object in a ServiceWorker.');
'resources/pushmessagedata-worker.js',
'PushMessageData initialization and accessor behaviour in a ServiceWorker.');
</script>
</body>
</html>
\ No newline at end of file
......@@ -26,7 +26,7 @@ test(function() {
test(function() {
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, 'blob');
assert_inherits(pushMessageData, 'json');
......
......@@ -11,14 +11,15 @@ test(function() {
assert_equals(event.bubbles, false);
assert_inherits(event, 'waitUntil');
var data = new PushMessageData('foo');
const textContents = 'Hello, world!';
var eventWithInit = new PushEvent('PushEvent',
{ cancelable: true,
bubbles: true,
data: data,
data: textContents,
});
assert_equals(eventWithInit.cancelable, true);
assert_equals(eventWithInit.bubbles, true);
assert_equals(eventWithInit.data, data);
assert_equals(eventWithInit.data.text(), textContents);
}, '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 @@
#include "config.h"
#include "modules/push_messaging/PushEvent.h"
#include "modules/push_messaging/PushEventInit.h"
namespace blink {
PushEvent::PushEvent()
......@@ -21,7 +23,7 @@ PushEvent::PushEvent(const AtomicString& type, const PushEventInit& initializer)
: ExtendableEvent(type, initializer)
{
if (initializer.hasData())
m_data = initializer.data();
m_data = PushMessageData::create(initializer.data());
}
PushEvent::~PushEvent()
......
......@@ -7,7 +7,6 @@
#include "modules/EventModules.h"
#include "modules/ModulesExport.h"
#include "modules/push_messaging/PushEventInit.h"
#include "modules/push_messaging/PushMessageData.h"
#include "modules/serviceworkers/ExtendableEvent.h"
#include "platform/heap/Handle.h"
......@@ -16,6 +15,8 @@
namespace blink {
class PushEventInit;
class MODULES_EXPORT PushEvent final : public ExtendableEvent {
DEFINE_WRAPPERTYPEINFO();
public:
......
......@@ -4,6 +4,9 @@
// 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 {
PushMessageData data;
PushMessageDataInit data;
};
......@@ -8,20 +8,46 @@
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/V8Binding.h"
#include "bindings/modules/v8/UnionTypesModules.h"
#include "core/dom/DOMArrayBuffer.h"
#include "core/fileapi/Blob.h"
#include "platform/blob/BlobData.h"
#include "wtf/text/TextEncoding.h"
#include <v8.h>
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(const String& messageData)
: m_messageData(messageData)
PushMessageData::PushMessageData(const char* data, unsigned bytesSize)
{
m_data.append(data, bytesSize);
}
PushMessageData::~PushMessageData()
......@@ -30,19 +56,19 @@ PushMessageData::~PushMessageData()
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
{
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
// provided, following the specification.
const long long blobSize = blobData->length();
return Blob::create(BlobDataHandle::create(blobData.release(), blobSize));
const long long byteLength = blobData->length();
return Blob::create(BlobDataHandle::create(blobData.release(), byteLength));
}
ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exceptionState) const
......@@ -50,7 +76,7 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce
v8::Isolate* isolate = scriptState->isolate();
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::Local<v8::Value> parsed;
......@@ -62,9 +88,9 @@ ScriptValue PushMessageData::json(ScriptState* scriptState, ExceptionState& exce
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)
......
......@@ -9,10 +9,12 @@
#include "bindings/core/v8/ScriptWrappable.h"
#include "modules/ModulesExport.h"
#include "platform/heap/Handle.h"
#include "wtf/Vector.h"
#include "wtf/text/WTFString.h"
namespace blink {
class ArrayBufferOrArrayBufferViewOrUSVString;
class Blob;
class DOMArrayBuffer;
class ExceptionState;
......@@ -26,26 +28,23 @@ public:
{
return new PushMessageData();
}
static PushMessageData* create(const String& messageData)
{
return new PushMessageData(messageData);
}
static PushMessageData* create(const String& data);
static PushMessageData* create(const ArrayBufferOrArrayBufferViewOrUSVString& data);
virtual ~PushMessageData();
PassRefPtr<DOMArrayBuffer> arrayBuffer() const;
Blob* blob() const;
ScriptValue json(ScriptState*, ExceptionState&) const;
const String& text() const;
String text() const;
DECLARE_TRACE();
private:
PushMessageData();
explicit PushMessageData(const String& messageData);
PushMessageData(const char* data, unsigned bytesSize);
String m_messageData;
Vector<char> m_data;
};
} // namespace blink
......
......@@ -7,7 +7,6 @@
typedef object JSON;
[
Constructor(USVString message),
Exposed=ServiceWorker,
GarbageCollected,
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