Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(191)

Side by Side Diff: third_party/WebKit/Source/modules/nfc/NFC.cpp

Issue 2885813002: [webnfc] Align nfc.push operation with the specification (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/nfc/NFC.h" 5 #include "modules/nfc/NFC.h"
6 6
7 #include "bindings/core/v8/ScriptPromiseResolver.h" 7 #include "bindings/core/v8/ScriptPromiseResolver.h"
8 #include "bindings/core/v8/V8ArrayBuffer.h" 8 #include "bindings/core/v8/V8ArrayBuffer.h"
9 #include "bindings/core/v8/V8StringResource.h" 9 #include "bindings/core/v8/V8StringResource.h"
10 #include "core/dom/DOMArrayBuffer.h" 10 #include "core/dom/DOMArrayBuffer.h"
11 #include "core/dom/DOMException.h" 11 #include "core/dom/DOMException.h"
12 #include "core/dom/Document.h" 12 #include "core/dom/Document.h"
13 #include "core/dom/ExceptionCode.h" 13 #include "core/dom/ExceptionCode.h"
14 #include "core/dom/ExecutionContext.h" 14 #include "core/dom/ExecutionContext.h"
15 #include "core/frame/LocalDOMWindow.h" 15 #include "core/frame/LocalDOMWindow.h"
16 #include "modules/nfc/NFCError.h" 16 #include "modules/nfc/NFCError.h"
17 #include "modules/nfc/NFCMessage.h" 17 #include "modules/nfc/NFCMessage.h"
18 #include "modules/nfc/NFCPushOptions.h" 18 #include "modules/nfc/NFCPushOptions.h"
19 #include "modules/nfc/NFCWatchOptions.h" 19 #include "modules/nfc/NFCWatchOptions.h"
20 #include "platform/mojo/MojoHelper.h" 20 #include "platform/mojo/MojoHelper.h"
21 #include "public/platform/InterfaceProvider.h" 21 #include "public/platform/InterfaceProvider.h"
22 #include "public/platform/Platform.h" 22 #include "public/platform/Platform.h"
23 23
24 namespace { 24 namespace {
25 const char kJsonMimePostfix[] = "+json";
25 const char kJsonMimePrefix[] = "application/"; 26 const char kJsonMimePrefix[] = "application/";
26 const char kJsonMimeType[] = "application/json"; 27 const char kJsonMimeType[] = "application/json";
27 const char kOpaqueMimeType[] = "application/octet-stream"; 28 const char kOpaqueMimeType[] = "application/octet-stream";
28 const char kPlainTextMimeType[] = "text/plain"; 29 const char kPlainTextMimeType[] = "text/plain";
29 const char kPlainTextMimePrefix[] = "text/"; 30 const char kPlainTextMimePrefix[] = "text/";
30 const char kCharSetUTF8[] = ";charset=UTF-8"; 31 const char kCharSetUTF8[] = ";charset=UTF-8";
31 } // anonymous namespace 32 } // anonymous namespace
32 33
33 // Mojo type converters 34 // Mojo type converters
34 namespace mojo { 35 namespace mojo {
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 347
347 return watchOptionsPtr; 348 return watchOptionsPtr;
348 } 349 }
349 }; 350 };
350 351
351 } // namespace mojo 352 } // namespace mojo
352 353
353 namespace blink { 354 namespace blink {
354 namespace { 355 namespace {
355 356
356 bool IsValidTextRecord(const NFCRecord& record) { 357 ScriptPromise RejectWithTypeError(ScriptState* script_state,
358 const String& message) {
359 return ScriptPromise::Reject(
360 script_state,
361 V8ThrowException::CreateTypeError(script_state->GetIsolate(), message));
362 }
363
364 ScriptPromise RejectWithDOMException(ScriptState* script_state,
365 ExceptionCode ec,
366 const String& message) {
367 return ScriptPromise::RejectWithDOMException(
368 script_state, DOMException::Create(ec, message));
369 }
370
371 ScriptPromise RejectIfInvalidTextRecord(ScriptState* script_state,
372 const NFCRecord& record) {
357 v8::Local<v8::Value> value = record.data().V8Value(); 373 v8::Local<v8::Value> value = record.data().V8Value();
358 if (!value->IsString() && 374 if (!value->IsString() &&
359 !(value->IsNumber() && !std::isnan(value.As<v8::Number>()->Value()))) 375 !(value->IsNumber() && !std::isnan(value.As<v8::Number>()->Value()))) {
360 return false; 376 return RejectWithTypeError(script_state,
377 "The data for the 'text' NFCRecords must be of "
378 "String or UnrestrctedDouble type.");
379 }
361 380
362 if (record.hasMediaType() && 381 if (record.hasMediaType() &&
363 !record.mediaType().StartsWith(kPlainTextMimePrefix, 382 !record.mediaType().StartsWith(kPlainTextMimePrefix,
364 kTextCaseUnicodeInsensitive)) 383 kTextCaseUnicodeInsensitive)) {
365 return false; 384 return RejectWithDOMException(script_state, kSyntaxError,
385 "Invalid media type for 'text' record.");
386 }
366 387
367 return true; 388 return ScriptPromise();
368 } 389 }
369 390
370 bool IsValidURLRecord(const NFCRecord& record) { 391 ScriptPromise RejectIfInvalidURLRecord(ScriptState* script_state,
371 if (!record.data().V8Value()->IsString()) 392 const NFCRecord& record) {
372 return false; 393 if (!record.data().V8Value()->IsString()) {
394 return RejectWithTypeError(
395 script_state,
396 "The data for the 'url' NFCRecord must be of String type.");
397 }
373 398
374 blink::V8StringResource<> string_resource = record.data().V8Value(); 399 blink::V8StringResource<> string_resource = record.data().V8Value();
375 if (!string_resource.Prepare()) 400 if (!string_resource.Prepare() || !KURL(KURL(), string_resource).IsValid()) {
376 return false; 401 return RejectWithDOMException(script_state, kSyntaxError,
402 "Cannot parse data for 'url' record.");
403 }
377 404
378 return KURL(KURL(), string_resource).IsValid(); 405 return ScriptPromise();
379 } 406 }
380 407
381 bool IsValidJSONRecord(const NFCRecord& record) { 408 ScriptPromise RejectIfInvalidJSONRecord(ScriptState* script_state,
409 const NFCRecord& record) {
382 v8::Local<v8::Value> value = record.data().V8Value(); 410 v8::Local<v8::Value> value = record.data().V8Value();
383 if (!value->IsObject() || value->IsArrayBuffer()) 411 if (!value->IsObject() || value->IsArrayBuffer()) {
384 return false; 412 return RejectWithTypeError(
413 script_state,
414 "The data for the 'json' NFCRecord must be of Object type.");
415 }
385 416
386 if (record.hasMediaType() && !record.mediaType().StartsWith( 417 // If JSON record has media type, it must be equal to "application/json" or
387 kJsonMimePrefix, kTextCaseASCIIInsensitive)) 418 // start with "application/" and end with "+json".
388 return false; 419 if (record.hasMediaType() &&
420 (record.mediaType() != kJsonMimeType &&
421 !(record.mediaType().StartsWith(kJsonMimePrefix,
422 kTextCaseASCIIInsensitive) &&
423 record.mediaType().EndsWith(kJsonMimePostfix,
424 kTextCaseASCIIInsensitive)))) {
425 return RejectWithDOMException(script_state, kSyntaxError,
426 "Invalid media type for 'json' record.");
427 }
389 428
390 return true; 429 return ScriptPromise();
391 } 430 }
392 431
393 bool IsValidOpaqueRecord(const NFCRecord& record) { 432 ScriptPromise RejectIfInvalidOpaqueRecord(ScriptState* script_state,
394 return record.data().V8Value()->IsArrayBuffer(); 433 const NFCRecord& record) {
434 if (!record.data().V8Value()->IsArrayBuffer()) {
435 return RejectWithTypeError(
436 script_state,
437 "The data for the 'opaque' NFCRecord must be of ArrayBuffer type.");
438 }
439
440 return ScriptPromise();
395 } 441 }
396 442
397 bool IsValidNFCRecord(const NFCRecord& record) { 443 ScriptPromise RejectIfInvalidNFCRecord(ScriptState* script_state,
444 const NFCRecord& record) {
398 device::nfc::mojom::blink::NFCRecordType type; 445 device::nfc::mojom::blink::NFCRecordType type;
399 if (record.hasRecordType()) { 446 if (record.hasRecordType()) {
400 type = mojo::toNFCRecordType(record.recordType()); 447 type = mojo::toNFCRecordType(record.recordType());
401 } else { 448 } else {
402 type = mojo::deduceRecordTypeFromDataType(record); 449 type = mojo::deduceRecordTypeFromDataType(record);
403 450
404 // https://w3c.github.io/web-nfc/#creating-web-nfc-message 451 // https://w3c.github.io/web-nfc/#creating-web-nfc-message
405 // If NFCRecord.recordType is not set and record type cannot be deduced 452 // If NFCRecord.recordType is not set and record type cannot be deduced
406 // from NFCRecord.data, reject promise with SyntaxError. 453 // from NFCRecord.data, reject promise with TypeError.
407 if (type == device::nfc::mojom::blink::NFCRecordType::EMPTY) 454 if (type == device::nfc::mojom::blink::NFCRecordType::EMPTY)
408 return false; 455 return RejectWithTypeError(script_state, "Unknown NFCRecord type.");
409 } 456 }
410 457
411 // Non-empty records must have data. 458 // Non-empty records must have data.
412 if (!record.hasData() && 459 if (!record.hasData() &&
413 (type != device::nfc::mojom::blink::NFCRecordType::EMPTY)) { 460 (type != device::nfc::mojom::blink::NFCRecordType::EMPTY)) {
414 return false; 461 return RejectWithTypeError(script_state,
462 "Nonempty NFCRecord must have data.");
415 } 463 }
416 464
417 switch (type) { 465 switch (type) {
418 case device::nfc::mojom::blink::NFCRecordType::TEXT: 466 case device::nfc::mojom::blink::NFCRecordType::TEXT:
419 return IsValidTextRecord(record); 467 return RejectIfInvalidTextRecord(script_state, record);
420 case device::nfc::mojom::blink::NFCRecordType::URL: 468 case device::nfc::mojom::blink::NFCRecordType::URL:
421 return IsValidURLRecord(record); 469 return RejectIfInvalidURLRecord(script_state, record);
422 case device::nfc::mojom::blink::NFCRecordType::JSON: 470 case device::nfc::mojom::blink::NFCRecordType::JSON:
423 return IsValidJSONRecord(record); 471 return RejectIfInvalidJSONRecord(script_state, record);
424 case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD: 472 case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD:
425 return IsValidOpaqueRecord(record); 473 return RejectIfInvalidOpaqueRecord(script_state, record);
426 case device::nfc::mojom::blink::NFCRecordType::EMPTY: 474 case device::nfc::mojom::blink::NFCRecordType::EMPTY:
427 return !record.hasData() && record.mediaType().IsEmpty(); 475 return ScriptPromise();
428 } 476 }
429 477
430 NOTREACHED(); 478 NOTREACHED();
431 return false; 479 return RejectWithTypeError(script_state,
480 "Invalid NFCRecordType was provided.");
432 } 481 }
433 482
434 bool IsValidNFCRecordArray(const HeapVector<NFCRecord>& records) { 483 ScriptPromise RejectIfInvalidNFCRecordArray(
435 if (records.IsEmpty()) 484 ScriptState* script_state,
436 return false; 485 const HeapVector<NFCRecord>& records) {
437
438 for (const auto& record : records) { 486 for (const auto& record : records) {
439 if (!IsValidNFCRecord(record)) 487 ScriptPromise isValidRecord =
440 return false; 488 RejectIfInvalidNFCRecord(script_state, record);
489 if (!isValidRecord.IsEmpty())
490 return isValidRecord;
441 } 491 }
442 492
443 return true; 493 return ScriptPromise();
444 } 494 }
445 495
446 bool IsValidNFCPushMessage(const NFCPushMessage& message) { 496 ScriptPromise RejectIfInvalidNFCPushMessage(
497 ScriptState* script_state,
498 const NFCPushMessage& push_message) {
447 // If NFCPushMessage of invalid type, reject promise with TypeError 499 // If NFCPushMessage of invalid type, reject promise with TypeError
448 if (!message.isNFCMessage() && !message.isString() && 500 if (!push_message.isNFCMessage() && !push_message.isString() &&
449 !message.isArrayBuffer()) 501 !push_message.isArrayBuffer()) {
450 return false; 502 return RejectWithTypeError(script_state,
503 "Invalid NFCPushMessage type was provided.");
504 }
451 505
452 if (message.isNFCMessage()) { 506 if (push_message.isNFCMessage()) {
453 // https://w3c.github.io/web-nfc/#the-push-method 507 // https://w3c.github.io/web-nfc/#the-push-method
454 // If NFCMessage.data is empty, reject promise with TypeError 508 // If NFCMessage.data is empty, reject promise with TypeError
455 if (!message.getAsNFCMessage().hasData()) 509 const NFCMessage& message = push_message.getAsNFCMessage();
456 return false; 510 if (!message.hasData() || message.data().IsEmpty()) {
511 return RejectWithTypeError(script_state,
512 "Empty NFCMessage was provided.");
513 }
457 514
458 return IsValidNFCRecordArray(message.getAsNFCMessage().data()); 515 return RejectIfInvalidNFCRecordArray(script_state, message.data());
459 } 516 }
460 517
461 return true; 518 return ScriptPromise();
462 } 519 }
463 520
464 bool SetURL(const String& origin, 521 bool SetURL(const String& origin,
465 device::nfc::mojom::blink::NFCMessagePtr& message) { 522 device::nfc::mojom::blink::NFCMessagePtr& message) {
466 KURL origin_url(kParsedURLString, origin); 523 KURL origin_url(kParsedURLString, origin);
467 524
468 if (!message->url.IsEmpty() && origin_url.CanSetPathname()) { 525 if (!message->url.IsEmpty() && origin_url.CanSetPathname()) {
469 origin_url.SetPath(message->url); 526 origin_url.SetPath(message->url);
470 } 527 }
471 528
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 671
615 // https://w3c.github.io/web-nfc/#writing-or-pushing-content 672 // https://w3c.github.io/web-nfc/#writing-or-pushing-content
616 // https://w3c.github.io/web-nfc/#dom-nfc-push 673 // https://w3c.github.io/web-nfc/#dom-nfc-push
617 ScriptPromise NFC::push(ScriptState* script_state, 674 ScriptPromise NFC::push(ScriptState* script_state,
618 const NFCPushMessage& push_message, 675 const NFCPushMessage& push_message,
619 const NFCPushOptions& options) { 676 const NFCPushOptions& options) {
620 ScriptPromise promise = RejectIfNotSupported(script_state); 677 ScriptPromise promise = RejectIfNotSupported(script_state);
621 if (!promise.IsEmpty()) 678 if (!promise.IsEmpty())
622 return promise; 679 return promise;
623 680
624 if (!IsValidNFCPushMessage(push_message)) { 681 ScriptPromise isValidMessage =
625 return ScriptPromise::Reject( 682 RejectIfInvalidNFCPushMessage(script_state, push_message);
626 script_state, V8ThrowException::CreateTypeError( 683 if (!isValidMessage.IsEmpty())
627 script_state->GetIsolate(), 684 return isValidMessage;
628 "Invalid NFCPushMessage type was provided."));
629 }
630 685
631 // https://w3c.github.io/web-nfc/#dom-nfc-push 686 // https://w3c.github.io/web-nfc/#dom-nfc-push
632 // 9. If timeout value is NaN or negative, reject promise with "TypeError" 687 // 9. If timeout value is NaN or negative, reject promise with "TypeError"
633 // and abort these steps. 688 // and abort these steps.
634 if (options.hasTimeout() && 689 if (options.hasTimeout() &&
635 (std::isnan(options.timeout()) || options.timeout() < 0)) { 690 (std::isnan(options.timeout()) || options.timeout() < 0)) {
636 return ScriptPromise::Reject( 691 return RejectWithTypeError(
637 script_state, 692 script_state, "Invalid NFCPushOptions.timeout value was provided.");
638 V8ThrowException::CreateTypeError(
639 script_state->GetIsolate(),
640 "Invalid NFCPushOptions.timeout value was provided."));
641 } 693 }
642 694
643 device::nfc::mojom::blink::NFCMessagePtr message = 695 device::nfc::mojom::blink::NFCMessagePtr message =
644 device::nfc::mojom::blink::NFCMessage::From(push_message); 696 device::nfc::mojom::blink::NFCMessage::From(push_message);
645 if (!message) 697 if (!message) {
646 return ScriptPromise::RejectWithDOMException( 698 return RejectWithDOMException(script_state, kSyntaxError,
647 script_state, DOMException::Create(kSyntaxError)); 699 "Cannot convert NFCMessage.");
700 }
648 701
649 if (!SetURL( 702 if (!SetURL(
650 ExecutionContext::From(script_state)->GetSecurityOrigin()->ToString(), 703 ExecutionContext::From(script_state)->GetSecurityOrigin()->ToString(),
651 message)) 704 message)) {
652 return ScriptPromise::RejectWithDOMException( 705 return RejectWithDOMException(script_state, kSyntaxError,
653 script_state, DOMException::Create(kSyntaxError)); 706 "Cannot set WebNFC Id.");
Reilly Grant (use Gerrit) 2017/05/16 15:21:04 Not this change but are "Cannot convert NFCMessage
Alexander Shalamov 2017/05/16 17:49:10 https://w3c.github.io/web-nfc/#creating-a-web-nfc-
shalamov 2017/05/17 13:12:26 - Added new test for JSON serialization and check
707 }
654 708
655 if (GetNFCMessageSize(message) > 709 if (GetNFCMessageSize(message) >
656 device::nfc::mojom::blink::NFCMessage::kMaxSize) { 710 device::nfc::mojom::blink::NFCMessage::kMaxSize) {
657 return ScriptPromise::RejectWithDOMException( 711 return RejectWithDOMException(script_state, kNotSupportedError,
658 script_state, DOMException::Create(kNotSupportedError)); 712 "NFCMessage exceeds maximum suppoted size.");
Reilly Grant (use Gerrit) 2017/05/16 15:21:04 supported
shalamov 2017/05/17 13:12:26 Done.
659 } 713 }
660 714
661 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); 715 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
662 requests_.insert(resolver); 716 requests_.insert(resolver);
663 auto callback = ConvertToBaseCallback(WTF::Bind(&NFC::OnRequestCompleted, 717 auto callback = ConvertToBaseCallback(WTF::Bind(&NFC::OnRequestCompleted,
664 WrapPersistent(this), 718 WrapPersistent(this),
665 WrapPersistent(resolver))); 719 WrapPersistent(resolver)));
666 nfc_->Push(std::move(message), 720 nfc_->Push(std::move(message),
667 device::nfc::mojom::blink::NFCPushOptions::From(options), 721 device::nfc::mojom::blink::NFCPushOptions::From(options),
668 callback); 722 callback);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
708 762
709 // https://w3c.github.io/web-nfc/#dom-nfc-cancelwatch 763 // https://w3c.github.io/web-nfc/#dom-nfc-cancelwatch
710 ScriptPromise NFC::cancelWatch(ScriptState* script_state, long id) { 764 ScriptPromise NFC::cancelWatch(ScriptState* script_state, long id) {
711 ScriptPromise promise = RejectIfNotSupported(script_state); 765 ScriptPromise promise = RejectIfNotSupported(script_state);
712 if (!promise.IsEmpty()) 766 if (!promise.IsEmpty())
713 return promise; 767 return promise;
714 768
715 if (id) { 769 if (id) {
716 callbacks_.erase(id); 770 callbacks_.erase(id);
717 } else { 771 } else {
718 return ScriptPromise::RejectWithDOMException( 772 return RejectWithDOMException(script_state, kNotFoundError,
719 script_state, DOMException::Create(kNotFoundError)); 773 "Provided watch id, cannot be found.");
Reilly Grant (use Gerrit) 2017/05/16 15:21:04 no comma
shalamov 2017/05/17 13:12:26 Done.
720 } 774 }
721 775
722 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); 776 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
723 requests_.insert(resolver); 777 requests_.insert(resolver);
724 nfc_->CancelWatch(id, ConvertToBaseCallback(WTF::Bind( 778 nfc_->CancelWatch(id, ConvertToBaseCallback(WTF::Bind(
725 &NFC::OnRequestCompleted, WrapPersistent(this), 779 &NFC::OnRequestCompleted, WrapPersistent(this),
726 WrapPersistent(resolver)))); 780 WrapPersistent(resolver))));
727 return resolver->Promise(); 781 return resolver->Promise();
728 } 782 }
729 783
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
810 return false; 864 return false;
811 } 865 }
812 866
813 return true; 867 return true;
814 } 868 }
815 869
816 ScriptPromise NFC::RejectIfNotSupported(ScriptState* script_state) { 870 ScriptPromise NFC::RejectIfNotSupported(ScriptState* script_state) {
817 String error_message; 871 String error_message;
818 if (!IsSupportedInContext(ExecutionContext::From(script_state), 872 if (!IsSupportedInContext(ExecutionContext::From(script_state),
819 error_message)) { 873 error_message)) {
820 return ScriptPromise::RejectWithDOMException( 874 return RejectWithDOMException(script_state, kSecurityError, error_message);
821 script_state, DOMException::Create(kSecurityError, error_message));
822 } 875 }
823 876
824 if (!nfc_) { 877 if (!nfc_) {
825 return ScriptPromise::RejectWithDOMException( 878 return RejectWithDOMException(script_state, kNotSupportedError,
826 script_state, DOMException::Create(kNotSupportedError)); 879 "WebNFC is not supported.");
827 } 880 }
828 881
829 return ScriptPromise(); 882 return ScriptPromise();
830 } 883 }
831 884
832 void NFC::OnWatchRegistered(MessageCallback* callback, 885 void NFC::OnWatchRegistered(MessageCallback* callback,
833 ScriptPromiseResolver* resolver, 886 ScriptPromiseResolver* resolver,
834 uint32_t id, 887 uint32_t id,
835 device::nfc::mojom::blink::NFCErrorPtr error) { 888 device::nfc::mojom::blink::NFCErrorPtr error) {
836 requests_.erase(resolver); 889 requests_.erase(resolver);
(...skipping 17 matching lines...) Expand all
854 } 907 }
855 908
856 DEFINE_TRACE(NFC) { 909 DEFINE_TRACE(NFC) {
857 PageVisibilityObserver::Trace(visitor); 910 PageVisibilityObserver::Trace(visitor);
858 ContextLifecycleObserver::Trace(visitor); 911 ContextLifecycleObserver::Trace(visitor);
859 visitor->Trace(requests_); 912 visitor->Trace(requests_);
860 visitor->Trace(callbacks_); 913 visitor->Trace(callbacks_);
861 } 914 }
862 915
863 } // namespace blink 916 } // namespace blink
OLDNEW
« third_party/WebKit/LayoutTests/nfc/push.html ('K') | « third_party/WebKit/LayoutTests/nfc/push.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698