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"
@@ -114,29 +117,19 @@ ScriptPromise NDEFReader::scan(ScriptState* script_state,
114
117
// to reader.[[Signal]]:
115
118
if (options->hasSignal ()) {
116
119
options->signal ()->AddAlgorithm (
117
- WTF::Bind (&NDEFReader::Abort , WrapPersistent (this )));
120
+ WTF::Bind (&NDEFReader::ReadAbort , WrapPersistent (this )));
118
121
}
119
122
120
123
GetPermissionService ()->RequestPermission (
121
124
CreatePermissionDescriptor (PermissionName::NFC),
122
125
LocalFrame::HasTransientUserActivation (frame),
123
- WTF::Bind (&NDEFReader::OnRequestPermission , WrapPersistent (this ),
126
+ WTF::Bind (&NDEFReader::ReadOnRequestPermission , WrapPersistent (this ),
124
127
WrapPersistent (options)));
125
128
return resolver_->Promise ();
126
129
}
127
130
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) {
131
+ void NDEFReader::ReadOnRequestPermission (const NDEFScanOptions* options,
132
+ PermissionStatus status) {
140
133
if (!resolver_) {
141
134
has_pending_scan_request_ = false ;
142
135
return ;
@@ -163,10 +156,10 @@ void NDEFReader::OnRequestPermission(const NDEFScanOptions* options,
163
156
164
157
GetNfcProxy ()->StartReading (
165
158
this ,
166
- WTF::Bind (&NDEFReader::OnScanRequestCompleted , WrapPersistent (this )));
159
+ WTF::Bind (&NDEFReader::ScanOnRequestCompleted , WrapPersistent (this )));
167
160
}
168
161
169
- void NDEFReader::OnScanRequestCompleted (
162
+ void NDEFReader::ScanOnRequestCompleted (
170
163
device::mojom::blink::NDEFErrorPtr error) {
171
164
has_pending_scan_request_ = false ;
172
165
if (!resolver_)
@@ -182,14 +175,6 @@ void NDEFReader::OnScanRequestCompleted(
182
175
resolver_.Clear ();
183
176
}
184
177
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
178
void NDEFReader::OnReading (const String& serial_number,
194
179
const device::mojom::blink::NDEFMessage& message) {
195
180
DCHECK (GetNfcProxy ()->IsReading (this ));
@@ -204,19 +189,6 @@ void NDEFReader::OnError(const String& message) {
204
189
DispatchEvent (*event);
205
190
}
206
191
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
192
void NDEFReader::ContextDestroyed () {
221
193
// If |resolver_| has already settled this rejection is silently ignored.
222
194
if (resolver_) {
@@ -228,7 +200,7 @@ void NDEFReader::ContextDestroyed() {
228
200
GetNfcProxy ()->StopReading (this );
229
201
}
230
202
231
- void NDEFReader::Abort () {
203
+ void NDEFReader::ReadAbort () {
232
204
if (resolver_) {
233
205
resolver_->Reject (MakeGarbageCollected<DOMException>(
234
206
DOMExceptionCode::kAbortError , " The NFC operation was cancelled." ));
@@ -238,9 +210,181 @@ void NDEFReader::Abort() {
238
210
GetNfcProxy ()->StopReading (this );
239
211
}
240
212
213
+ // https://w3c.github.io/web-nfc/#writing-content
214
+ // https://w3c.github.io/web-nfc/#the-write-method
215
+ ScriptPromise NDEFReader::write (ScriptState* script_state,
216
+ const NDEFMessageSource& write_message,
217
+ const NDEFWriteOptions* options,
218
+ ExceptionState& exception_state) {
219
+ LocalDOMWindow* window = script_state->ContextIsValid ()
220
+ ? LocalDOMWindow::From (script_state)
221
+ : nullptr ;
222
+ // https://w3c.github.io/web-nfc/#security-policies
223
+ // WebNFC API must be only accessible from top level browsing context.
224
+ if (!window || !window->GetFrame ()->IsMainFrame ()) {
225
+ exception_state.ThrowDOMException (DOMExceptionCode::kNotAllowedError ,
226
+ " NFC interfaces are only avaliable "
227
+ " in a top-level browsing context" );
228
+ return ScriptPromise ();
229
+ }
230
+
231
+ if (options->hasSignal () && options->signal ()->aborted ()) {
232
+ // If signal’s aborted flag is set, then reject p with an "AbortError"
233
+ // DOMException and return p.
234
+ exception_state.ThrowDOMException (DOMExceptionCode::kAbortError ,
235
+ " The NFC operation was cancelled." );
236
+ return ScriptPromise ();
237
+ }
238
+
239
+ // Step 11.2: Run "create NDEF message", if this throws an exception,
240
+ // reject p with that exception and abort these steps.
241
+ NDEFMessage* ndef_message =
242
+ NDEFMessage::Create (window, write_message, exception_state);
243
+ if (exception_state.HadException ()) {
244
+ return ScriptPromise ();
245
+ }
246
+
247
+ auto message = device::mojom::blink::NDEFMessage::From (ndef_message);
248
+ DCHECK (message);
249
+
250
+ auto * resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
251
+ requests_.insert (resolver);
252
+ InitNfcProxyIfNeeded ();
253
+ GetPermissionService ()->RequestPermission (
254
+ CreatePermissionDescriptor (PermissionName::NFC),
255
+ LocalFrame::HasTransientUserActivation (window->GetFrame ()),
256
+ WTF::Bind (&NDEFReader::WriteOnRequestPermission, WrapPersistent (this ),
257
+ WrapPersistent (resolver), WrapPersistent (options),
258
+ std::move (message)));
259
+
260
+ return resolver->Promise ();
261
+ }
262
+
263
+ void NDEFReader::WriteOnRequestPermission (
264
+ ScriptPromiseResolver* resolver,
265
+ const NDEFWriteOptions* options,
266
+ device::mojom::blink::NDEFMessagePtr message,
267
+ PermissionStatus status) {
268
+ if (status != PermissionStatus::GRANTED) {
269
+ resolver->Reject (MakeGarbageCollected<DOMException>(
270
+ DOMExceptionCode::kNotAllowedError , " NFC permission request denied." ));
271
+ return ;
272
+ }
273
+
274
+ if (options->hasSignal () && options->signal ()->aborted ()) {
275
+ resolver->Reject (MakeGarbageCollected<DOMException>(
276
+ DOMExceptionCode::kAbortError , " The NFC operation was cancelled." ));
277
+ return ;
278
+ }
279
+
280
+ // If signal is not null, then add the abort steps to signal.
281
+ if (options->hasSignal () && !options->signal ()->aborted ()) {
282
+ options->signal ()->AddAlgorithm (WTF::Bind (&NDEFReader::WriteAbort,
283
+ WrapPersistent (this ),
284
+ WrapPersistent (resolver)));
285
+ }
286
+
287
+ UseCounter::Count (GetExecutionContext (), WebFeature::kWebNfcNdefWriterWrite );
288
+ // TODO(https://crbug.com/994936) remove when origin trial is complete.
289
+ UseCounter::Count (GetExecutionContext (), WebFeature::kWebNfcAPI );
290
+
291
+ auto callback = WTF::Bind (&NDEFReader::WriteOnRequestCompleted,
292
+ WrapPersistent (this ), WrapPersistent (resolver));
293
+ nfc_proxy_->Push (std::move (message),
294
+ device::mojom::blink::NDEFWriteOptions::From (options),
295
+ std::move (callback));
296
+ }
297
+
298
+ void NDEFReader::WriteOnRequestCompleted (
299
+ 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