6
6
7
7
#include < utility>
8
8
9
+ #include " mojo/public/cpp/bindings/callback_helpers.h"
9
10
#include " services/device/public/mojom/nfc.mojom-blink.h"
10
- #include " third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
11
11
#include " third_party/blink/renderer/bindings/core/v8/source_location.h"
12
+ #include " third_party/blink/renderer/bindings/modules/v8/string_or_array_buffer_or_array_buffer_view_or_ndef_message_init.h"
12
13
#include " third_party/blink/renderer/bindings/modules/v8/v8_ndef_scan_options.h"
14
+ #include " third_party/blink/renderer/bindings/modules/v8/v8_ndef_write_options.h"
13
15
#include " third_party/blink/renderer/core/dom/abort_signal.h"
14
16
#include " third_party/blink/renderer/core/dom/dom_exception.h"
15
17
#include " third_party/blink/renderer/core/events/error_event.h"
19
21
#include " third_party/blink/renderer/modules/nfc/ndef_message.h"
20
22
#include " third_party/blink/renderer/modules/nfc/ndef_reading_event.h"
21
23
#include " third_party/blink/renderer/modules/nfc/nfc_proxy.h"
24
+ #include " third_party/blink/renderer/modules/nfc/nfc_type_converters.h"
22
25
#include " third_party/blink/renderer/modules/nfc/nfc_utils.h"
23
26
#include " third_party/blink/renderer/modules/permissions/permission_utils.h"
24
27
#include " third_party/blink/renderer/platform/instrumentation/use_counter.h"
@@ -49,7 +52,8 @@ NDEFReader* NDEFReader::Create(ExecutionContext* context) {
49
52
}
50
53
51
54
NDEFReader::NDEFReader (ExecutionContext* context)
52
- : ExecutionContextLifecycleObserver(context), permission_service_(context) {
55
+ : ExecutionContextLifecycleObserver(context),
56
+ permission_service_ (context) {
53
57
// Call GetNFCProxy to create a proxy. This guarantees no allocation will
54
58
// be needed when calling HasPendingActivity later during gc tracing.
55
59
GetNfcProxy ();
@@ -114,29 +118,19 @@ ScriptPromise NDEFReader::scan(ScriptState* script_state,
114
118
// to reader.[[Signal]]:
115
119
if (options->hasSignal ()) {
116
120
options->signal ()->AddAlgorithm (
117
- WTF::Bind (&NDEFReader::Abort , WrapPersistent (this )));
121
+ WTF::Bind (&NDEFReader::ReadAbort , WrapPersistent (this )));
118
122
}
119
123
120
124
GetPermissionService ()->RequestPermission (
121
125
CreatePermissionDescriptor (PermissionName::NFC),
122
126
LocalFrame::HasTransientUserActivation (frame),
123
- WTF::Bind (&NDEFReader::OnRequestPermission , WrapPersistent (this ),
127
+ WTF::Bind (&NDEFReader::ReadOnRequestPermission , WrapPersistent (this ),
124
128
WrapPersistent (options)));
125
129
return resolver_->Promise ();
126
130
}
127
131
128
- PermissionService* NDEFReader::GetPermissionService () {
129
- if (!permission_service_.is_bound ()) {
130
- ConnectToPermissionService (
131
- GetExecutionContext (),
132
- permission_service_.BindNewPipeAndPassReceiver (
133
- GetExecutionContext ()->GetTaskRunner (TaskType::kMiscPlatformAPI )));
134
- }
135
- return permission_service_.get ();
136
- }
137
-
138
- void NDEFReader::OnRequestPermission (const NDEFScanOptions* options,
139
- PermissionStatus status) {
132
+ void NDEFReader::ReadOnRequestPermission (const NDEFScanOptions* options,
133
+ PermissionStatus status) {
140
134
if (!resolver_) {
141
135
has_pending_scan_request_ = false ;
142
136
return ;
@@ -163,10 +157,10 @@ void NDEFReader::OnRequestPermission(const NDEFScanOptions* options,
163
157
164
158
GetNfcProxy ()->StartReading (
165
159
this ,
166
- WTF::Bind (&NDEFReader::OnScanRequestCompleted , WrapPersistent (this )));
160
+ WTF::Bind (&NDEFReader::ScanOnRequestCompleted , WrapPersistent (this )));
167
161
}
168
162
169
- void NDEFReader::OnScanRequestCompleted (
163
+ void NDEFReader::ScanOnRequestCompleted (
170
164
device::mojom::blink::NDEFErrorPtr error) {
171
165
has_pending_scan_request_ = false ;
172
166
if (!resolver_)
@@ -182,14 +176,6 @@ void NDEFReader::OnScanRequestCompleted(
182
176
resolver_.Clear ();
183
177
}
184
178
185
- void NDEFReader::Trace (Visitor* visitor) const {
186
- visitor->Trace (permission_service_);
187
- visitor->Trace (resolver_);
188
- EventTargetWithInlineData::Trace (visitor);
189
- ActiveScriptWrappable::Trace (visitor);
190
- ExecutionContextLifecycleObserver::Trace (visitor);
191
- }
192
-
193
179
void NDEFReader::OnReading (const String& serial_number,
194
180
const device::mojom::blink::NDEFMessage& message) {
195
181
DCHECK (GetNfcProxy ()->IsReading (this ));
@@ -204,19 +190,6 @@ void NDEFReader::OnError(const String& message) {
204
190
DispatchEvent (*event);
205
191
}
206
192
207
- void NDEFReader::OnMojoConnectionError () {
208
- // If |resolver_| has already settled this rejection is silently ignored.
209
- if (resolver_) {
210
- resolver_->Reject (NDEFErrorTypeToDOMException (
211
- device::mojom::blink::NDEFErrorType::NOT_SUPPORTED,
212
- kNotSupportedOrPermissionDenied ));
213
- resolver_.Clear ();
214
- }
215
-
216
- // Dispatches an error event.
217
- OnError (kNotSupportedOrPermissionDenied );
218
- }
219
-
220
193
void NDEFReader::ContextDestroyed () {
221
194
// If |resolver_| has already settled this rejection is silently ignored.
222
195
if (resolver_) {
@@ -228,7 +201,7 @@ void NDEFReader::ContextDestroyed() {
228
201
GetNfcProxy ()->StopReading (this );
229
202
}
230
203
231
- void NDEFReader::Abort () {
204
+ void NDEFReader::ReadAbort () {
232
205
if (resolver_) {
233
206
resolver_->Reject (MakeGarbageCollected<DOMException>(
234
207
DOMExceptionCode::kAbortError , " The NFC operation was cancelled." ));
@@ -238,9 +211,180 @@ void NDEFReader::Abort() {
238
211
GetNfcProxy ()->StopReading (this );
239
212
}
240
213
214
+ // https://w3c.github.io/web-nfc/#writing-content
215
+ // https://w3c.github.io/web-nfc/#the-write-method
216
+ ScriptPromise NDEFReader::write (ScriptState* script_state,
217
+ const NDEFMessageSource& write_message,
218
+ const NDEFWriteOptions* options,
219
+ ExceptionState& exception_state) {
220
+ LocalDOMWindow* window = script_state->ContextIsValid ()
221
+ ? LocalDOMWindow::From (script_state)
222
+ : nullptr ;
223
+ // https://w3c.github.io/web-nfc/#security-policies
224
+ // WebNFC API must be only accessible from top level browsing context.
225
+ if (!window || !window->GetFrame ()->IsMainFrame ()) {
226
+ exception_state.ThrowDOMException (DOMExceptionCode::kNotAllowedError ,
227
+ " NFC interfaces are only avaliable "
228
+ " in a top-level browsing context" );
229
+ return ScriptPromise ();
230
+ }
231
+
232
+ if (options->hasSignal () && options->signal ()->aborted ()) {
233
+ // If signal’s aborted flag is set, then reject p with an "AbortError"
234
+ // DOMException and return p.
235
+ exception_state.ThrowDOMException (DOMExceptionCode::kAbortError ,
236
+ " The NFC operation was cancelled." );
237
+ return ScriptPromise ();
238
+ }
239
+
240
+ // Step 11.2: Run "create NDEF message", if this throws an exception,
241
+ // reject p with that exception and abort these steps.
242
+ NDEFMessage* ndef_message =
243
+ NDEFMessage::Create (window, write_message, exception_state);
244
+ if (exception_state.HadException ()) {
245
+ return ScriptPromise ();
246
+ }
247
+
248
+ auto message = device::mojom::blink::NDEFMessage::From (ndef_message);
249
+ DCHECK (message);
250
+
251
+ auto * resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
252
+ requests_.insert (resolver);
253
+ InitNfcProxyIfNeeded ();
254
+ GetPermissionService ()->RequestPermission (
255
+ CreatePermissionDescriptor (PermissionName::NFC),
256
+ LocalFrame::HasTransientUserActivation (window->GetFrame ()),
257
+ WTF::Bind (&NDEFReader::WriteOnRequestPermission, WrapPersistent (this ),
258
+ WrapPersistent (resolver), WrapPersistent (options),
259
+ std::move (message)));
260
+
261
+ return resolver->Promise ();
262
+ }
263
+
264
+ void NDEFReader::WriteOnRequestPermission (
265
+ ScriptPromiseResolver* resolver,
266
+ const NDEFWriteOptions* options,
267
+ device::mojom::blink::NDEFMessagePtr message,
268
+ PermissionStatus status) {
269
+ if (status != PermissionStatus::GRANTED) {
270
+ resolver->Reject (MakeGarbageCollected<DOMException>(
271
+ DOMExceptionCode::kNotAllowedError , " NFC permission request denied." ));
272
+ return ;
273
+ }
274
+
275
+ if (options->hasSignal () && options->signal ()->aborted ()) {
276
+ resolver->Reject (MakeGarbageCollected<DOMException>(
277
+ DOMExceptionCode::kAbortError , " The NFC operation was cancelled." ));
278
+ return ;
279
+ }
280
+
281
+ // If signal is not null, then add the abort steps to signal.
282
+ if (options->hasSignal () && !options->signal ()->aborted ()) {
283
+ options->signal ()->AddAlgorithm (WTF::Bind (&NDEFReader::WriteAbort,
284
+ WrapPersistent (this ),
285
+ WrapPersistent (resolver)));
286
+ }
287
+
288
+ UseCounter::Count (GetExecutionContext (), WebFeature::kWebNfcNdefWriterWrite );
289
+ // TODO(https://crbug.com/994936) remove when origin trial is complete.
290
+ UseCounter::Count (GetExecutionContext (), WebFeature::kWebNfcAPI );
291
+
292
+ auto callback = WTF::Bind (&NDEFReader::WriteOnRequestCompleted,
293
+ WrapPersistent (this ), WrapPersistent (resolver));
294
+ nfc_proxy_->Push (std::move (message),
295
+ device::mojom::blink::NDEFWriteOptions::From (options),
296
+ std::move (callback));
297
+ }
298
+
299
+ void NDEFReader::WriteOnRequestCompleted (ScriptPromiseResolver* resolver,
300
+ device::mojom::blink::NDEFErrorPtr error) {
301
+ DCHECK (requests_.Contains (resolver));
302
+
303
+ requests_.erase (resolver);
304
+
305
+ if (error.is_null ()) {
306
+ resolver->Resolve ();
307
+ } else {
308
+ resolver->Reject (
309
+ NDEFErrorTypeToDOMException (error->error_type , error->error_message ));
310
+ }
311
+ }
312
+
313
+ void NDEFReader::WriteAbort (ScriptPromiseResolver* resolver) {
314
+ // |nfc_proxy_| could be null on Mojo connection failure, simply ignore the
315
+ // abort request in this case.
316
+ if (!nfc_proxy_)
317
+ return ;
318
+
319
+ // OnRequestCompleted() should always be called whether the push operation is
320
+ // cancelled successfully or not. So do nothing for the cancelled callback.
321
+ nfc_proxy_->CancelPush (device::mojom::blink::NFC::CancelPushCallback ());
322
+ }
323
+
324
+ void NDEFReader::InitNfcProxyIfNeeded () {
325
+ // Init NfcProxy if needed.
326
+ if (nfc_proxy_) {
327
+ nfc_proxy_->AddWriter (this );
328
+ return ;
329
+ }
330
+
331
+ nfc_proxy_ = NFCProxy::From (*To<LocalDOMWindow>(GetExecutionContext ()));
332
+ DCHECK (nfc_proxy_);
333
+
334
+ // Add the writer to proxy's writer list for mojo connection error
335
+ // notification.
336
+ nfc_proxy_->AddWriter (this );
337
+ }
338
+
241
339
NFCProxy* NDEFReader::GetNfcProxy () const {
242
340
DCHECK (GetExecutionContext ());
243
341
return NFCProxy::From (*To<LocalDOMWindow>(GetExecutionContext ()));
244
342
}
245
343
344
+ void NDEFReader::Trace (Visitor* visitor) const {
345
+ visitor->Trace (permission_service_);
346
+ visitor->Trace (nfc_proxy_);
347
+ visitor->Trace (resolver_);
348
+ visitor->Trace (requests_);
349
+ EventTargetWithInlineData::Trace (visitor);
350
+ ActiveScriptWrappable::Trace (visitor);
351
+ ExecutionContextLifecycleObserver::Trace (visitor);
352
+ }
353
+
354
+ PermissionService* NDEFReader::GetPermissionService () {
355
+ if (!permission_service_.is_bound ()) {
356
+ ConnectToPermissionService (
357
+ GetExecutionContext (),
358
+ permission_service_.BindNewPipeAndPassReceiver (
359
+ GetExecutionContext ()->GetTaskRunner (TaskType::kMiscPlatformAPI )));
360
+ }
361
+ return permission_service_.get ();
362
+ }
363
+
364
+ void NDEFReader::ReadOnMojoConnectionError () {
365
+ // If |resolver_| has already settled this rejection is silently ignored.
366
+ if (resolver_) {
367
+ resolver_->Reject (NDEFErrorTypeToDOMException (
368
+ device::mojom::blink::NDEFErrorType::NOT_SUPPORTED,
369
+ kNotSupportedOrPermissionDenied ));
370
+ resolver_.Clear ();
371
+ }
372
+
373
+ // Dispatches an error event.
374
+ OnError (kNotSupportedOrPermissionDenied );
375
+ }
376
+
377
+ void NDEFReader::WriteOnMojoConnectionError () {
378
+ nfc_proxy_.Clear ();
379
+
380
+ // If the mojo connection breaks, all push requests will be rejected with a
381
+ // default error.
382
+ for (ScriptPromiseResolver* resolver : requests_) {
383
+ resolver->Reject (NDEFErrorTypeToDOMException (
384
+ device::mojom::blink::NDEFErrorType::NOT_SUPPORTED,
385
+ kNotSupportedOrPermissionDenied ));
386
+ }
387
+ requests_.clear ();
388
+ }
389
+
246
390
} // namespace blink
0 commit comments