Commit 21f1c419 authored by Leon Han's avatar Leon Han Committed by Commit Bot

[webnfc] Make NDEFReader#scan() return a Promise

Previously NDEFReader#scan() returns void and an NDEFErrorEvent will be
dispatched in case that the scan operation cannot be started
successfully.

This is not a well accepted pattern, and also to align with
NDEFWriter#push(), this CL makes NDEFReader#scan() return a Promise
instead.

Note that now NDEFErrorEvent is only used to notify Mojo disconnection,
a follow-up CL will remove it completely by using ErrorEvent instead.

The spec change:
https://github.com/w3c/web-nfc/pull/398
https://github.com/w3c/web-nfc/pull/432

BUG=520391

Change-Id: I1477258ab70f7e40da31ea8795d63125b6a13af0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1899586
Commit-Queue: Leon Han <leon.han@intel.com>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#716904}
parent e4b9e599
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include "services/device/public/mojom/nfc.mojom-blink.h" #include "services/device/public/mojom/nfc.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/abort_signal.h" #include "third_party/blink/renderer/core/dom/abort_signal.h"
#include "third_party/blink/renderer/modules/event_target_modules.h" #include "third_party/blink/renderer/modules/event_target_modules.h"
#include "third_party/blink/renderer/modules/nfc/ndef_error_event.h" #include "third_party/blink/renderer/modules/nfc/ndef_error_event.h"
...@@ -19,6 +20,19 @@ ...@@ -19,6 +20,19 @@
namespace blink { namespace blink {
namespace {
void OnScanRequestCompleted(ScriptPromiseResolver* resolver,
device::mojom::blink::NDEFErrorPtr error) {
if (error) {
resolver->Reject(NDEFErrorTypeToDOMException(error->error_type));
return;
}
resolver->Resolve();
}
} // namespace
// static // static
NDEFReader* NDEFReader::Create(ExecutionContext* context) { NDEFReader* NDEFReader::Create(ExecutionContext* context) {
return MakeGarbageCollected<NDEFReader>(context); return MakeGarbageCollected<NDEFReader>(context);
...@@ -47,39 +61,70 @@ bool NDEFReader::HasPendingActivity() const { ...@@ -47,39 +61,70 @@ bool NDEFReader::HasPendingActivity() const {
} }
// https://w3c.github.io/web-nfc/#the-scan-method // https://w3c.github.io/web-nfc/#the-scan-method
void NDEFReader::scan(const NDEFScanOptions* options) { ScriptPromise NDEFReader::scan(ScriptState* script_state,
if (!CheckSecurity()) const NDEFScanOptions* options,
return; ExceptionState& exception_state) {
ExecutionContext* execution_context = GetExecutionContext();
// https://w3c.github.io/web-nfc/#security-policies
// WebNFC API must be only accessible from top level browsing context.
if (!execution_context || !To<Document>(execution_context)->IsInMainFrame()) {
exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
"NFC interfaces are only avaliable "
"in a top-level browsing context");
return ScriptPromise();
}
if (options->hasSignal()) { // 7. If reader.[[Signal]]'s aborted flag is set, then reject p with a
// 6. If reader.[[Signal]]'s aborted flag is set, then return. // "AbortError" DOMException and return p.
if (options->signal()->aborted()) if (options->hasSignal() && options->signal()->aborted()) {
return; exception_state.ThrowDOMException(DOMExceptionCode::kAbortError,
"The NFC operation was cancelled.");
// 7. If reader.[[Signal]] is not null, then add the following abort steps return ScriptPromise();
// to reader.[[Signal]]:
options->signal()->AddAlgorithm(
WTF::Bind(&NDEFReader::Abort, WrapPersistent(this)));
} }
// Step 8.4, if the url is not an empty string and it is not a valid URL // 9.4 If the reader.[[Id]] is not an empty string and it is not a valid URL
// pattern, fire a NDEFErrorEvent with "SyntaxError" DOMException, then // pattern, then reject p with a "SyntaxError" DOMException and return p.
// return. // TODO(https://crbug.com/520391): Instead of NDEFScanOptions#url, introduce
// and use NDEFScanOptions#id to do the filtering after we add support for
// writing NDEFRecord#id.
if (options->hasURL() && !options->url().IsEmpty()) { if (options->hasURL() && !options->url().IsEmpty()) {
KURL pattern_url(options->url()); KURL pattern_url(options->url());
if (!pattern_url.IsValid() || pattern_url.Protocol() != "https") { if (!pattern_url.IsValid() || pattern_url.Protocol() != "https") {
DispatchEvent(*MakeGarbageCollected<NDEFErrorEvent>( exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
event_type_names::kError, MakeGarbageCollected<DOMException>( "Invalid URL pattern was provided.");
DOMExceptionCode::kSyntaxError, return ScriptPromise();
"Invalid URL pattern was provided.")));
return;
} }
} }
GetNfcProxy()->StartReading(this, options); // TODO(https://crbug.com/520391): With the note in
// https://w3c.github.io/web-nfc/#the-ndefreader-and-ndefwriter-objects,
// successive invocations of NDEFReader.scan() with new options should replace
// existing filters. For now we just reject this new scan() when there is an
// ongoing filter active.
if (GetNfcProxy()->IsReading(this)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"There is already a scan() operation ongoing.");
return ScriptPromise();
}
resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
// 8. If reader.[[Signal]] is not null, then add the following abort steps to
// reader.[[Signal]]:
if (options->hasSignal()) {
options->signal()->AddAlgorithm(WTF::Bind(&NDEFReader::Abort,
WrapPersistent(this),
WrapPersistent(resolver_.Get())));
}
GetNfcProxy()->StartReading(
this, options,
WTF::Bind(&OnScanRequestCompleted, WrapPersistent(resolver_.Get())));
return resolver_->Promise();
} }
void NDEFReader::Trace(blink::Visitor* visitor) { void NDEFReader::Trace(blink::Visitor* visitor) {
visitor->Trace(resolver_);
EventTargetWithInlineData::Trace(visitor); EventTargetWithInlineData::Trace(visitor);
ActiveScriptWrappable::Trace(visitor); ActiveScriptWrappable::Trace(visitor);
ContextLifecycleObserver::Trace(visitor); ContextLifecycleObserver::Trace(visitor);
...@@ -98,29 +143,31 @@ void NDEFReader::OnError(device::mojom::blink::NDEFErrorType error) { ...@@ -98,29 +143,31 @@ void NDEFReader::OnError(device::mojom::blink::NDEFErrorType error) {
event_type_names::kError, NDEFErrorTypeToDOMException(error))); event_type_names::kError, NDEFErrorTypeToDOMException(error)));
} }
void NDEFReader::ContextDestroyed(ExecutionContext*) { void NDEFReader::OnMojoConnectionError() {
GetNfcProxy()->StopReading(this); // If |resolver_| has already settled this rejection is silently ignored.
if (resolver_) {
resolver_->Reject(NDEFErrorTypeToDOMException(
device::mojom::blink::NDEFErrorType::NOT_SUPPORTED));
}
// Dispatches an error event.
OnError(device::mojom::blink::NDEFErrorType::NOT_SUPPORTED);
} }
void NDEFReader::Abort() { void NDEFReader::ContextDestroyed(ExecutionContext*) {
// If |resolver_| has already settled this rejection is silently ignored.
if (resolver_) {
resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kAbortError,
"The execution context is going to be gone."));
}
GetNfcProxy()->StopReading(this); GetNfcProxy()->StopReading(this);
} }
bool NDEFReader::CheckSecurity() { void NDEFReader::Abort(ScriptPromiseResolver* resolver) {
ExecutionContext* execution_context = GetExecutionContext(); // If |resolver| has already settled this rejection is silently ignored.
if (!execution_context) resolver->Reject(MakeGarbageCollected<DOMException>(
return false; DOMExceptionCode::kAbortError, "The NFC operation was cancelled."));
// https://w3c.github.io/web-nfc/#security-policies GetNfcProxy()->StopReading(this);
// WebNFC API must be only accessible from top level browsing context.
if (!To<Document>(execution_context)->IsInMainFrame()) {
DispatchEvent(*MakeGarbageCollected<NDEFErrorEvent>(
event_type_names::kError,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotAllowedError,
"NFC interfaces are only avaliable "
"in a top-level browsing context")));
return false;
}
return true;
} }
NFCProxy* NDEFReader::GetNfcProxy() const { NFCProxy* NDEFReader::GetNfcProxy() const {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "services/device/public/mojom/nfc.mojom-blink-forward.h" #include "services/device/public/mojom/nfc.mojom-blink-forward.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h" #include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
#include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/modules_export.h"
...@@ -17,6 +18,7 @@ namespace blink { ...@@ -17,6 +18,7 @@ namespace blink {
class ExecutionContext; class ExecutionContext;
class NFCProxy; class NFCProxy;
class NDEFScanOptions; class NDEFScanOptions;
class ScriptPromiseResolver;
class MODULES_EXPORT NDEFReader : public EventTargetWithInlineData, class MODULES_EXPORT NDEFReader : public EventTargetWithInlineData,
public ActiveScriptWrappable<NDEFReader>, public ActiveScriptWrappable<NDEFReader>,
...@@ -39,24 +41,30 @@ class MODULES_EXPORT NDEFReader : public EventTargetWithInlineData, ...@@ -39,24 +41,30 @@ class MODULES_EXPORT NDEFReader : public EventTargetWithInlineData,
DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError) DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError)
DEFINE_ATTRIBUTE_EVENT_LISTENER(reading, kReading) DEFINE_ATTRIBUTE_EVENT_LISTENER(reading, kReading)
void scan(const NDEFScanOptions*); ScriptPromise scan(ScriptState*, const NDEFScanOptions*, ExceptionState&);
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
// Called by NFCProxy for dispatching events. // Called by NFCProxy for dispatching events.
virtual void OnReading(const String& serial_number, virtual void OnReading(const String& serial_number,
const device::mojom::blink::NDEFMessage& message); const device::mojom::blink::NDEFMessage&);
virtual void OnError(device::mojom::blink::NDEFErrorType error); virtual void OnError(device::mojom::blink::NDEFErrorType);
// Called by NFCProxy for notification about connection error.
void OnMojoConnectionError();
private: private:
// ContextLifecycleObserver overrides. // ContextLifecycleObserver overrides.
void ContextDestroyed(ExecutionContext*) override; void ContextDestroyed(ExecutionContext*) override;
void Abort(); void Abort(ScriptPromiseResolver*);
NFCProxy* GetNfcProxy() const; NFCProxy* GetNfcProxy() const;
bool CheckSecurity(); // |resolver_| is kept here to handle Mojo connection failures because in that
// case the callback passed to Watch() won't be called and
// mojo::WrapCallbackWithDefaultInvokeIfNotRun() is forbidden in Blink.
Member<ScriptPromiseResolver> resolver_;
}; };
} // namespace blink } // namespace blink
......
...@@ -15,5 +15,6 @@ ...@@ -15,5 +15,6 @@
attribute EventHandler onreading; attribute EventHandler onreading;
attribute EventHandler onerror; attribute EventHandler onerror;
void scan(optional NDEFScanOptions options={}); [CallWith=ScriptState, RaisesException] Promise<void> scan(
optional NDEFScanOptions options={});
}; };
...@@ -52,16 +52,16 @@ void NFCProxy::Trace(blink::Visitor* visitor) { ...@@ -52,16 +52,16 @@ void NFCProxy::Trace(blink::Visitor* visitor) {
} }
void NFCProxy::StartReading(NDEFReader* reader, void NFCProxy::StartReading(NDEFReader* reader,
const NDEFScanOptions* options) { const NDEFScanOptions* options,
device::mojom::blink::NFC::WatchCallback callback) {
DCHECK(reader); DCHECK(reader);
if (readers_.Contains(reader)) DCHECK(!readers_.Contains(reader));
return;
EnsureMojoConnection(); EnsureMojoConnection();
nfc_remote_->Watch( nfc_remote_->Watch(
device::mojom::blink::NDEFScanOptions::From(options), next_watch_id_, device::mojom::blink::NDEFScanOptions::From(options), next_watch_id_,
WTF::Bind(&NFCProxy::OnReaderRegistered, WrapPersistent(this), WTF::Bind(&NFCProxy::OnReaderRegistered, WrapPersistent(this),
WrapPersistent(reader), next_watch_id_)); WrapPersistent(reader), next_watch_id_, std::move(callback)));
readers_.insert(reader, next_watch_id_); readers_.insert(reader, next_watch_id_);
next_watch_id_++; next_watch_id_++;
} }
...@@ -119,9 +119,11 @@ void NFCProxy::OnWatch(const Vector<uint32_t>& watch_ids, ...@@ -119,9 +119,11 @@ void NFCProxy::OnWatch(const Vector<uint32_t>& watch_ids,
} }
} }
void NFCProxy::OnReaderRegistered(NDEFReader* reader, void NFCProxy::OnReaderRegistered(
uint32_t watch_id, NDEFReader* reader,
device::mojom::blink::NDEFErrorPtr error) { uint32_t watch_id,
device::mojom::blink::NFC::WatchCallback callback,
device::mojom::blink::NDEFErrorPtr error) {
DCHECK(reader); DCHECK(reader);
// |reader| may have already stopped reading. // |reader| may have already stopped reading.
if (!readers_.Contains(reader)) if (!readers_.Contains(reader))
...@@ -134,13 +136,15 @@ void NFCProxy::OnReaderRegistered(NDEFReader* reader, ...@@ -134,13 +136,15 @@ void NFCProxy::OnReaderRegistered(NDEFReader* reader,
return; return;
if (error) { if (error) {
reader->OnError(error->error_type);
readers_.erase(reader); readers_.erase(reader);
std::move(callback).Run(std::move(error));
return; return;
} }
// It's good the watch request has been accepted, we do nothing here but just std::move(callback).Run(nullptr);
// wait for message notifications in OnWatch().
// It's good the watch request has been accepted, next we just wait for
// message notifications in OnWatch().
} }
void NFCProxy::PageVisibilityChanged() { void NFCProxy::PageVisibilityChanged() {
...@@ -182,9 +186,7 @@ void NFCProxy::OnMojoConnectionError() { ...@@ -182,9 +186,7 @@ void NFCProxy::OnMojoConnectionError() {
// Notify all active readers about the connection error and clear the list. // Notify all active readers about the connection error and clear the list.
ReaderMap readers = std::move(readers_); ReaderMap readers = std::move(readers_);
for (auto& pair : readers) { for (auto& pair : readers) {
// The reader may call StopReading() to remove itself from |readers_| when pair.key->OnMojoConnectionError();
// handling the error.
pair.key->OnError(device::mojom::blink::NDEFErrorType::NOT_SUPPORTED);
} }
// Each connection maintains its own watch ID numbering, so reset to 1 on // Each connection maintains its own watch ID numbering, so reset to 1 on
......
...@@ -45,7 +45,9 @@ class MODULES_EXPORT NFCProxy final : public GarbageCollected<NFCProxy>, ...@@ -45,7 +45,9 @@ class MODULES_EXPORT NFCProxy final : public GarbageCollected<NFCProxy>,
// collected. // collected.
void AddWriter(NDEFWriter*); void AddWriter(NDEFWriter*);
void StartReading(NDEFReader*, const NDEFScanOptions*); void StartReading(NDEFReader*,
const NDEFScanOptions*,
device::mojom::blink::NFC::WatchCallback);
void StopReading(NDEFReader*); void StopReading(NDEFReader*);
bool IsReading(const NDEFReader*); bool IsReading(const NDEFReader*);
void Push(device::mojom::blink::NDEFMessagePtr, void Push(device::mojom::blink::NDEFMessagePtr,
...@@ -61,6 +63,7 @@ class MODULES_EXPORT NFCProxy final : public GarbageCollected<NFCProxy>, ...@@ -61,6 +63,7 @@ class MODULES_EXPORT NFCProxy final : public GarbageCollected<NFCProxy>,
void OnReaderRegistered(NDEFReader*, void OnReaderRegistered(NDEFReader*,
uint32_t watch_id, uint32_t watch_id,
device::mojom::blink::NFC::WatchCallback,
device::mojom::blink::NDEFErrorPtr); device::mojom::blink::NDEFErrorPtr);
// Implementation of PageVisibilityObserver. // Implementation of PageVisibilityObserver.
......
...@@ -53,7 +53,6 @@ class MockNDEFReader : public NDEFReader { ...@@ -53,7 +53,6 @@ class MockNDEFReader : public NDEFReader {
MOCK_METHOD2(OnReading, MOCK_METHOD2(OnReading,
void(const String& serial_number, void(const String& serial_number,
const device::mojom::blink::NDEFMessage& message)); const device::mojom::blink::NDEFMessage& message));
MOCK_METHOD1(OnError, void(device::mojom::blink::NDEFErrorType error));
}; };
class FakeNfcService : public device::mojom::blink::NFC { class FakeNfcService : public device::mojom::blink::NFC {
...@@ -96,6 +95,10 @@ class FakeNfcService : public device::mojom::blink::NFC { ...@@ -96,6 +95,10 @@ class FakeNfcService : public device::mojom::blink::NFC {
tag_message_ = std::move(message); tag_message_ = std::move(message);
} }
void set_watch_error(device::mojom::blink::NDEFErrorPtr error) {
watch_error_ = std::move(error);
}
WTF::Vector<uint32_t> GetWatches() { WTF::Vector<uint32_t> GetWatches() {
WTF::Vector<uint32_t> ids; WTF::Vector<uint32_t> ids;
for (auto& pair : watches_) { for (auto& pair : watches_) {
...@@ -123,6 +126,10 @@ class FakeNfcService : public device::mojom::blink::NFC { ...@@ -123,6 +126,10 @@ class FakeNfcService : public device::mojom::blink::NFC {
void Watch(device::mojom::blink::NDEFScanOptionsPtr options, void Watch(device::mojom::blink::NDEFScanOptionsPtr options,
uint32_t id, uint32_t id,
WatchCallback callback) override { WatchCallback callback) override {
if (watch_error_) {
std::move(callback).Run(watch_error_.Clone());
return;
}
watches_.emplace(id, std::move(options)); watches_.emplace(id, std::move(options));
std::move(callback).Run(nullptr); std::move(callback).Run(nullptr);
} }
...@@ -141,6 +148,7 @@ class FakeNfcService : public device::mojom::blink::NFC { ...@@ -141,6 +148,7 @@ class FakeNfcService : public device::mojom::blink::NFC {
void SuspendNFCOperations() override {} void SuspendNFCOperations() override {}
void ResumeNFCOperations() override {} void ResumeNFCOperations() override {}
device::mojom::blink::NDEFErrorPtr watch_error_;
device::mojom::blink::NDEFMessagePtr tag_message_; device::mojom::blink::NDEFMessagePtr tag_message_;
mojo::Remote<device::mojom::blink::NFCClient> client_; mojo::Remote<device::mojom::blink::NFCClient> client_;
std::map<uint32_t, device::mojom::blink::NDEFScanOptionsPtr> watches_; std::map<uint32_t, device::mojom::blink::NDEFScanOptionsPtr> watches_;
...@@ -167,8 +175,6 @@ class NFCProxyTest : public PageTestBase { ...@@ -167,8 +175,6 @@ class NFCProxyTest : public PageTestBase {
FakeNfcService* nfc_service() { return nfc_service_.get(); } FakeNfcService* nfc_service() { return nfc_service_.get(); }
void DestroyNfcService() { nfc_service_.reset(); }
private: private:
std::unique_ptr<FakeNfcService> nfc_service_; std::unique_ptr<FakeNfcService> nfc_service_;
}; };
...@@ -180,10 +186,18 @@ TEST_F(NFCProxyTest, SuccessfulPath) { ...@@ -180,10 +186,18 @@ TEST_F(NFCProxyTest, SuccessfulPath) {
scan_options->setURL(kTestUrl); scan_options->setURL(kTestUrl);
auto* reader = MakeGarbageCollected<MockNDEFReader>(&document); auto* reader = MakeGarbageCollected<MockNDEFReader>(&document);
nfc_proxy->StartReading(reader, scan_options); {
EXPECT_TRUE(nfc_proxy->IsReading(reader)); base::RunLoop loop;
test::RunPendingTasks(); nfc_proxy->StartReading(reader, scan_options,
EXPECT_EQ(nfc_service()->GetWatches().size(), 1u); base::BindLambdaForTesting(
[&](device::mojom::blink::NDEFErrorPtr error) {
EXPECT_TRUE(error.is_null());
loop.Quit();
}));
EXPECT_TRUE(nfc_proxy->IsReading(reader));
loop.Run();
EXPECT_EQ(nfc_service()->GetWatches().size(), 1u);
}
// Construct a NDEFMessagePtr // Construct a NDEFMessagePtr
auto message = device::mojom::blink::NDEFMessage::New(); auto message = device::mojom::blink::NDEFMessage::New();
...@@ -191,24 +205,26 @@ TEST_F(NFCProxyTest, SuccessfulPath) { ...@@ -191,24 +205,26 @@ TEST_F(NFCProxyTest, SuccessfulPath) {
auto record = device::mojom::blink::NDEFRecord::New(); auto record = device::mojom::blink::NDEFRecord::New();
WTF::Vector<uint8_t> record_data( WTF::Vector<uint8_t> record_data(
{0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}); {0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10});
record->record_type = "opaque"; record->record_type = "mime";
record->data = WTF::Vector<uint8_t>(record_data); record->data = WTF::Vector<uint8_t>(record_data);
message->data.push_back(std::move(record)); message->data.push_back(std::move(record));
base::RunLoop loop; {
EXPECT_CALL(*reader, OnReading(String(kFakeNfcTagSerialNumber), base::RunLoop loop;
MessageEquals(record_data))) EXPECT_CALL(*reader, OnReading(String(kFakeNfcTagSerialNumber),
.WillOnce(Invoke([&](const String& serial_number, MessageEquals(record_data)))
const device::mojom::blink::NDEFMessage& message) { .WillOnce(Invoke([&](const String& serial_number,
loop.Quit(); const device::mojom::blink::NDEFMessage& message) {
})); loop.Quit();
}));
nfc_proxy->Push(
std::move(message), /*options=*/nullptr, nfc_proxy->Push(std::move(message), /*options=*/nullptr,
base::BindLambdaForTesting([&](device::mojom::blink::NDEFErrorPtr error) { base::BindLambdaForTesting(
nfc_service()->TriggerWatchEvent(); [&](device::mojom::blink::NDEFErrorPtr error) {
})); nfc_service()->TriggerWatchEvent();
loop.Run(); }));
loop.Run();
}
nfc_proxy->StopReading(reader); nfc_proxy->StopReading(reader);
EXPECT_FALSE(nfc_proxy->IsReading(reader)); EXPECT_FALSE(nfc_proxy->IsReading(reader));
...@@ -223,16 +239,23 @@ TEST_F(NFCProxyTest, ErrorPath) { ...@@ -223,16 +239,23 @@ TEST_F(NFCProxyTest, ErrorPath) {
scan_options->setURL(kTestUrl); scan_options->setURL(kTestUrl);
auto* reader = MakeGarbageCollected<MockNDEFReader>(&document); auto* reader = MakeGarbageCollected<MockNDEFReader>(&document);
nfc_proxy->StartReading(reader, scan_options); // Make the fake NFC service return an error for the incoming watch request.
EXPECT_TRUE(nfc_proxy->IsReading(reader)); nfc_service()->set_watch_error(device::mojom::blink::NDEFError::New(
test::RunPendingTasks(); device::mojom::blink::NDEFErrorType::NOT_READABLE));
base::RunLoop loop; base::RunLoop loop;
EXPECT_CALL(*reader, OnError(_)) nfc_proxy->StartReading(
.WillOnce( reader, scan_options,
Invoke([&](device::mojom::blink::NDEFErrorType) { loop.Quit(); })); base::BindLambdaForTesting([&](device::mojom::blink::NDEFErrorPtr error) {
DestroyNfcService(); // We got the error prepared before.
EXPECT_FALSE(error.is_null());
EXPECT_EQ(error->error_type,
device::mojom::blink::NDEFErrorType::NOT_READABLE);
loop.Quit();
}));
EXPECT_TRUE(nfc_proxy->IsReading(reader));
loop.Run(); loop.Run();
EXPECT_EQ(nfc_service()->GetWatches().size(), 0u);
EXPECT_FALSE(nfc_proxy->IsReading(reader)); EXPECT_FALSE(nfc_proxy->IsReading(reader));
} }
......
...@@ -9,15 +9,13 @@ ...@@ -9,15 +9,13 @@
promise_test(async t => { promise_test(async t => {
const reader = new NDEFReader(); const reader = new NDEFReader();
reader.scan();
const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]); const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
const promise = readerWatcher.wait_for("reading").then(event => {
return await new Promise((resolve, reject) => { if (document.hidden) reject();
readerWatcher.wait_for("reading").then(event => { resolve();
if (document.hidden) reject();
resolve();
});
}); });
await reader.scan();
await promise;
}, "Test NDEFReader.onreading is not fired when document is hidden"); }, "Test NDEFReader.onreading is not fired when document is hidden");
</script> </script>
......
...@@ -17,7 +17,7 @@ nfc_test(async (t, mockNFC) => { ...@@ -17,7 +17,7 @@ nfc_test(async (t, mockNFC) => {
assert_true(event instanceof NDEFReadingEvent); assert_true(event instanceof NDEFReadingEvent);
controller.abort(); controller.abort();
}); });
reader.scan({ signal: controller.signal }); await reader.scan({ signal: controller.signal });
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
iframe.src = 'resources/support-iframe.html'; iframe.src = 'resources/support-iframe.html';
...@@ -40,4 +40,4 @@ nfc_test(async (t, mockNFC) => { ...@@ -40,4 +40,4 @@ nfc_test(async (t, mockNFC) => {
}, 'Test that NDEFWriter.scan is not suspended if iframe gains focus.'); }, 'Test that NDEFWriter.scan is not suspended if iframe gains focus.');
</script> </script>
</body> </body>
\ No newline at end of file
...@@ -316,18 +316,18 @@ nfc_test(async (t, mockNFC) => { ...@@ -316,18 +316,18 @@ nfc_test(async (t, mockNFC) => {
const message = createMessage([createTextRecord(test_text_data)]); const message = createMessage([createTextRecord(test_text_data)]);
const controller = new AbortController(); const controller = new AbortController();
const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]); const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
const promise = readerWatcher.wait_for("reading").then(event => { await reader.scan({ signal: controller.signal });
controller.abort();
assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
});
reader.scan({ signal: controller.signal });
const writer = new NDEFWriter(); const writer = new NDEFWriter();
await writer.push(test_text_data, { ignoreRead: false }); await writer.push(test_text_data, { ignoreRead: false });
mockNFC.setReadingMessage(message);
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage()); assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
await promise;
mockNFC.setReadingMessage(message);
await readerWatcher.wait_for("reading").then(event => {
controller.abort();
assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
});
}, "NDEFWriter.push should read data when ignoreRead is false."); }, "NDEFWriter.push should read data when ignoreRead is false.");
nfc_test(async (t, mockNFC) => { nfc_test(async (t, mockNFC) => {
...@@ -335,7 +335,7 @@ nfc_test(async (t, mockNFC) => { ...@@ -335,7 +335,7 @@ nfc_test(async (t, mockNFC) => {
const message = createMessage([createTextRecord(test_text_data)]); const message = createMessage([createTextRecord(test_text_data)]);
// Ignore reading if NDEFPushOptions.ignoreRead is true // Ignore reading if NDEFPushOptions.ignoreRead is true
reader.onreading = t.unreached_func("reading event should not be fired."); reader.onreading = t.unreached_func("reading event should not be fired.");
reader.scan(); await reader.scan();
const writer = new NDEFWriter(); const writer = new NDEFWriter();
await writer.push(test_text_data, { ignoreRead: true }); await writer.push(test_text_data, { ignoreRead: true });
......
This is a testharness.js-based test. This is a testharness.js-based test.
Found 72 tests; 70 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 72 tests; 71 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup PASS idl_test setup
PASS idl_test validation PASS idl_test validation
PASS NDEFMessage interface: existence and properties of interface object PASS NDEFMessage interface: existence and properties of interface object
...@@ -53,7 +53,7 @@ PASS NDEFReader interface: existence and properties of interface prototype objec ...@@ -53,7 +53,7 @@ PASS NDEFReader interface: existence and properties of interface prototype objec
PASS NDEFReader interface: existence and properties of interface prototype object's @@unscopables property PASS NDEFReader interface: existence and properties of interface prototype object's @@unscopables property
PASS NDEFReader interface: attribute onerror PASS NDEFReader interface: attribute onerror
PASS NDEFReader interface: attribute onreading PASS NDEFReader interface: attribute onreading
FAIL NDEFReader interface: operation scan(NDEFScanOptions) assert_unreached: Throws "TypeError: Illegal invocation" instead of rejecting promise Reached unreachable code PASS NDEFReader interface: operation scan(NDEFScanOptions)
PASS NDEFReader must be primary interface of new NDEFReader(); PASS NDEFReader must be primary interface of new NDEFReader();
PASS Stringification of new NDEFReader(); PASS Stringification of new NDEFReader();
PASS NDEFReader interface: new NDEFReader(); must inherit property "onerror" with the proper type PASS NDEFReader interface: new NDEFReader(); must inherit property "onerror" with the proper type
......
...@@ -174,22 +174,20 @@ function testNDEFScanOptions(message, scanOptions, unmatchedScanOptions, desc) { ...@@ -174,22 +174,20 @@ function testNDEFScanOptions(message, scanOptions, unmatchedScanOptions, desc) {
const reader2 = new NDEFReader(); const reader2 = new NDEFReader();
const controller = new AbortController(); const controller = new AbortController();
mockNFC.setReadingMessage(message);
// Reading from unmatched reader will not be triggered // Reading from unmatched reader will not be triggered
reader1.onreading = t.unreached_func("reading event should not be fired."); reader1.onreading = t.unreached_func("reading event should not be fired.");
unmatchedScanOptions.signal = controller.signal; unmatchedScanOptions.signal = controller.signal;
reader1.scan(unmatchedScanOptions); await reader1.scan(unmatchedScanOptions);
const readerWatcher = new EventWatcher(t, reader2, ["reading", "error"]); const readerWatcher = new EventWatcher(t, reader2, ["reading", "error"]);
const promise = readerWatcher.wait_for("reading").then(event => { const promise = readerWatcher.wait_for("reading").then(event => {
controller.abort(); controller.abort();
assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message)); assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
}); });
// NDEFReader#scan() asynchronously dispatches the onreading event.
scanOptions.signal = controller.signal; scanOptions.signal = controller.signal;
reader2.scan(scanOptions); await reader2.scan(scanOptions);
mockNFC.setReadingMessage(message);
await promise; await promise;
}, desc); }, desc);
} }
...@@ -200,19 +198,16 @@ function testReadingMultiMessages( ...@@ -200,19 +198,16 @@ function testReadingMultiMessages(
const reader = new NDEFReader(); const reader = new NDEFReader();
const controller = new AbortController(); const controller = new AbortController();
const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]); const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
const promise = readerWatcher.wait_for("reading").then(event => { const promise = readerWatcher.wait_for("reading").then(event => {
controller.abort(); controller.abort();
assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message)); assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
}); });
// NDEFReader#scan() asynchronously dispatches the onreading event.
scanOptions.signal = controller.signal; scanOptions.signal = controller.signal;
reader.scan(scanOptions); await reader.scan(scanOptions);
// Unmatched message will not be read // Unmatched message will not be read
mockNFC.setReadingMessage(unmatchedMessage); mockNFC.setReadingMessage(unmatchedMessage);
mockNFC.setReadingMessage(message); mockNFC.setReadingMessage(message);
await promise; await promise;
}, desc); }, desc);
} }
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