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

Side by Side Diff: content/browser/devtools/render_frame_devtools_agent_host.cc

Issue 2400863002: Merge to 2840 "[DevTools] Avoid current_ and pending_ being the same host in RenderFrameDevToolsAge… (Closed)
Patch Set: Created 4 years, 2 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
« no previous file with comments | « content/browser/devtools/render_frame_devtools_agent_host.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 "content/browser/devtools/render_frame_devtools_agent_host.h" 5 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
6 6
7 #include <tuple> 7 #include <tuple>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 RenderFrameHost* current) { 320 RenderFrameHost* current) {
321 if (IsBrowserSideNavigationEnabled()) 321 if (IsBrowserSideNavigationEnabled())
322 return; 322 return;
323 323
324 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(pending); 324 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(pending);
325 if (!agent_host) 325 if (!agent_host)
326 return; 326 return;
327 if (agent_host->pending_ && agent_host->pending_->host() == pending) { 327 if (agent_host->pending_ && agent_host->pending_->host() == pending) {
328 DCHECK(agent_host->current_ && agent_host->current_->host() == current); 328 DCHECK(agent_host->current_ && agent_host->current_->host() == current);
329 agent_host->DiscardPending(); 329 agent_host->DiscardPending();
330 DCHECK(agent_host->CheckConsistency());
330 } 331 }
331 } 332 }
332 333
333 // static 334 // static
334 void RenderFrameDevToolsAgentHost::OnBeforeNavigation( 335 void RenderFrameDevToolsAgentHost::OnBeforeNavigation(
335 RenderFrameHost* current, RenderFrameHost* pending) { 336 RenderFrameHost* current, RenderFrameHost* pending) {
336 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(current); 337 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(current);
337 if (agent_host) 338 if (agent_host)
338 agent_host->AboutToNavigateRenderFrame(current, pending); 339 agent_host->AboutToNavigateRenderFrame(current, pending);
339 } 340 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 service_worker_handler_( 383 service_worker_handler_(
383 new devtools::service_worker::ServiceWorkerHandler()), 384 new devtools::service_worker::ServiceWorkerHandler()),
384 storage_handler_(new devtools::storage::StorageHandler()), 385 storage_handler_(new devtools::storage::StorageHandler()),
385 tracing_handler_(new devtools::tracing::TracingHandler( 386 tracing_handler_(new devtools::tracing::TracingHandler(
386 devtools::tracing::TracingHandler::Renderer, 387 devtools::tracing::TracingHandler::Renderer,
387 host->GetFrameTreeNodeId(), 388 host->GetFrameTreeNodeId(),
388 GetIOContext())), 389 GetIOContext())),
389 emulation_handler_(nullptr), 390 emulation_handler_(nullptr),
390 frame_trace_recorder_(nullptr), 391 frame_trace_recorder_(nullptr),
391 protocol_handler_(new DevToolsProtocolHandler(this)), 392 protocol_handler_(new DevToolsProtocolHandler(this)),
393 handlers_frame_host_(nullptr),
392 current_frame_crashed_(false), 394 current_frame_crashed_(false),
393 pending_handle_(nullptr), 395 pending_handle_(nullptr),
394 frame_tree_node_(host->frame_tree_node()) { 396 frame_tree_node_(host->frame_tree_node()) {
395 DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); 397 DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
396 dispatcher->SetBrowserHandler(browser_handler_.get()); 398 dispatcher->SetBrowserHandler(browser_handler_.get());
397 dispatcher->SetDOMHandler(dom_handler_.get()); 399 dispatcher->SetDOMHandler(dom_handler_.get());
398 dispatcher->SetInputHandler(input_handler_.get()); 400 dispatcher->SetInputHandler(input_handler_.get());
399 dispatcher->SetInspectorHandler(inspector_handler_.get()); 401 dispatcher->SetInspectorHandler(inspector_handler_.get());
400 dispatcher->SetIOHandler(io_handler_.get()); 402 dispatcher->SetIOHandler(io_handler_.get());
401 dispatcher->SetNetworkHandler(network_handler_.get()); 403 dispatcher->SetNetworkHandler(network_handler_.get());
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
565 // If the navigation is not tracked, return; 567 // If the navigation is not tracked, return;
566 if (navigating_handles_.count(navigation_handle) == 0) 568 if (navigating_handles_.count(navigation_handle) == 0)
567 return; 569 return;
568 570
569 RenderFrameHostImpl* render_frame_host_impl = 571 RenderFrameHostImpl* render_frame_host_impl =
570 static_cast<RenderFrameHostImpl*>( 572 static_cast<RenderFrameHostImpl*>(
571 navigation_handle->GetRenderFrameHost()); 573 navigation_handle->GetRenderFrameHost());
572 if (current_->host() != render_frame_host_impl || current_frame_crashed_) { 574 if (current_->host() != render_frame_host_impl || current_frame_crashed_) {
573 SetPending(render_frame_host_impl); 575 SetPending(render_frame_host_impl);
574 pending_handle_ = navigation_handle; 576 pending_handle_ = navigation_handle;
577 // Commit when navigating the same frame after crash, avoiding the same
578 // host in current_ and pending_.
579 if (current_->host() == render_frame_host_impl) {
580 pending_handle_ = nullptr;
581 CommitPending();
582 }
575 } 583 }
584 DCHECK(CheckConsistency());
576 } 585 }
577 586
578 void RenderFrameDevToolsAgentHost::DidFinishNavigation( 587 void RenderFrameDevToolsAgentHost::DidFinishNavigation(
579 NavigationHandle* navigation_handle) { 588 NavigationHandle* navigation_handle) {
580 if (!IsBrowserSideNavigationEnabled()) 589 if (!IsBrowserSideNavigationEnabled())
581 return; 590 return;
582 591
583 // If the navigation is not tracked, return; 592 // If the navigation is not tracked, return;
584 if (navigating_handles_.count(navigation_handle) == 0) 593 if (navigating_handles_.count(navigation_handle) == 0)
585 return; 594 return;
586 595
587 // Now that the navigation is finished, remove the handle from the list of 596 // Now that the navigation is finished, remove the handle from the list of
588 // navigating handles. 597 // navigating handles.
589 navigating_handles_.erase(navigation_handle); 598 navigating_handles_.erase(navigation_handle);
590 599
591 if (pending_handle_ == navigation_handle) { 600 if (pending_handle_ == navigation_handle) {
592 // This navigation handle did set the pending FrameHostHolder. 601 // This navigation handle did set the pending FrameHostHolder.
593 DCHECK(pending_); 602 DCHECK(pending_);
594 if (navigation_handle->HasCommitted()) { 603 if (navigation_handle->HasCommitted()) {
595 DCHECK(pending_->host() == navigation_handle->GetRenderFrameHost()); 604 DCHECK(pending_->host() == navigation_handle->GetRenderFrameHost());
596 CommitPending(); 605 CommitPending();
597 } else { 606 } else {
598 DiscardPending(); 607 DiscardPending();
599 } 608 }
600 pending_handle_ = nullptr; 609 pending_handle_ = nullptr;
601 } 610 }
602 DispatchBufferedProtocolMessagesIfNecessary(); 611 DispatchBufferedProtocolMessagesIfNecessary();
603 612
613 DCHECK(CheckConsistency());
604 if (navigation_handle->HasCommitted()) 614 if (navigation_handle->HasCommitted())
605 service_worker_handler_->UpdateHosts(); 615 service_worker_handler_->UpdateHosts();
606 } 616 }
607 617
608 void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame( 618 void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame(
609 RenderFrameHost* old_host, 619 RenderFrameHost* old_host,
610 RenderFrameHost* new_host) { 620 RenderFrameHost* new_host) {
611 if (IsBrowserSideNavigationEnabled()) 621 if (IsBrowserSideNavigationEnabled())
612 return; 622 return;
613 623
614 DCHECK(!pending_ || pending_->host() != old_host); 624 DCHECK(!pending_ || pending_->host() != old_host);
615 if (!current_ || current_->host() != old_host) 625 if (!current_ || current_->host() != old_host) {
626 DCHECK(CheckConsistency());
616 return; 627 return;
617 if (old_host == new_host && !current_frame_crashed_) 628 }
629 if (old_host == new_host && !current_frame_crashed_) {
630 DCHECK(CheckConsistency());
618 return; 631 return;
632 }
619 DCHECK(!pending_); 633 DCHECK(!pending_);
620 SetPending(static_cast<RenderFrameHostImpl*>(new_host)); 634 SetPending(static_cast<RenderFrameHostImpl*>(new_host));
635 // Commit when navigating the same frame after crash, avoiding the same
636 // host in current_ and pending_.
637 if (old_host == new_host)
638 CommitPending();
639 DCHECK(CheckConsistency());
621 } 640 }
622 641
623 void RenderFrameDevToolsAgentHost::AboutToNavigate( 642 void RenderFrameDevToolsAgentHost::AboutToNavigate(
624 NavigationHandle* navigation_handle) { 643 NavigationHandle* navigation_handle) {
625 if (!IsBrowserSideNavigationEnabled()) 644 if (!IsBrowserSideNavigationEnabled())
626 return; 645 return;
627 DCHECK(current_); 646 DCHECK(current_);
628 navigating_handles_.insert(navigation_handle); 647 navigating_handles_.insert(navigation_handle);
648 DCHECK(CheckConsistency());
629 } 649 }
630 650
631 void RenderFrameDevToolsAgentHost::RenderFrameHostChanged( 651 void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
632 RenderFrameHost* old_host, 652 RenderFrameHost* old_host,
633 RenderFrameHost* new_host) { 653 RenderFrameHost* new_host) {
634 if (IsBrowserSideNavigationEnabled()) 654 if (IsBrowserSideNavigationEnabled())
635 return; 655 return;
636 656
637 DCHECK(!pending_ || pending_->host() != old_host); 657 DCHECK(!pending_ || pending_->host() != old_host);
638 if (!current_ || current_->host() != old_host) 658 if (!current_ || current_->host() != old_host) {
659 DCHECK(CheckConsistency());
639 return; 660 return;
661 }
640 662
641 // AboutToNavigateRenderFrame was not called for renderer-initiated 663 // AboutToNavigateRenderFrame was not called for renderer-initiated
642 // navigation. 664 // navigation.
643 if (!pending_) 665 if (!pending_)
644 SetPending(static_cast<RenderFrameHostImpl*>(new_host)); 666 SetPending(static_cast<RenderFrameHostImpl*>(new_host));
645
646 CommitPending(); 667 CommitPending();
668 DCHECK(CheckConsistency());
647 } 669 }
648 670
649 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) { 671 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) {
650 if (pending_ && pending_->host() == rfh) { 672 if (pending_ && pending_->host() == rfh) {
651 if (!IsBrowserSideNavigationEnabled()) 673 if (!IsBrowserSideNavigationEnabled())
652 DiscardPending(); 674 DiscardPending();
675 DCHECK(CheckConsistency());
653 return; 676 return;
654 } 677 }
655 678
656 if (current_ && current_->host() == rfh) 679 if (current_ && current_->host() == rfh)
657 DestroyOnRenderFrameGone(); // |this| may be deleted at this point. 680 DestroyOnRenderFrameGone(); // |this| may be deleted at this point.
658 } 681 }
659 682
660 void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) { 683 void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) {
661 if (!current_frame_crashed_) 684 if (!current_frame_crashed_)
662 FrameDeleted(rfh); 685 FrameDeleted(rfh);
686 else
687 DCHECK(CheckConsistency());
663 } 688 }
664 689
665 void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() { 690 void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
666 DCHECK(current_); 691 DCHECK(current_);
667 scoped_refptr<RenderFrameDevToolsAgentHost> protect(this); 692 scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
668 UpdateProtocolHandlers(nullptr); 693 UpdateProtocolHandlers(nullptr);
669 if (IsAttached()) 694 if (IsAttached())
670 OnClientDetached(); 695 OnClientDetached();
671 HostClosed(); 696 HostClosed();
672 pending_.reset(); 697 pending_.reset();
673 current_.reset(); 698 current_.reset();
674 frame_tree_node_ = nullptr; 699 frame_tree_node_ = nullptr;
675 pending_handle_ = nullptr; 700 pending_handle_ = nullptr;
676 WebContentsObserver::Observe(nullptr); 701 WebContentsObserver::Observe(nullptr);
677 Release(); 702 Release();
678 } 703 }
679 704
705 bool RenderFrameDevToolsAgentHost::CheckConsistency() {
706 if (current_ && pending_ && current_->host() == pending_->host())
707 return false;
708 if (IsBrowserSideNavigationEnabled())
709 return true;
710 if (!frame_tree_node_)
711 return !handlers_frame_host_;
712 RenderFrameHostManager* manager = frame_tree_node_->render_manager();
713 return handlers_frame_host_ == manager->current_frame_host() ||
714 handlers_frame_host_ == manager->pending_frame_host();
715 }
716
680 void RenderFrameDevToolsAgentHost::CreatePowerSaveBlocker() { 717 void RenderFrameDevToolsAgentHost::CreatePowerSaveBlocker() {
681 #if defined(OS_ANDROID) 718 #if defined(OS_ANDROID)
682 power_save_blocker_.reset(new device::PowerSaveBlocker( 719 power_save_blocker_.reset(new device::PowerSaveBlocker(
683 device::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, 720 device::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
684 device::PowerSaveBlocker::kReasonOther, "DevTools", 721 device::PowerSaveBlocker::kReasonOther, "DevTools",
685 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), 722 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI),
686 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE))); 723 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE)));
687 if (web_contents()->GetNativeView()) { 724 if (web_contents()->GetNativeView()) {
688 view_weak_factory_.reset(new base::WeakPtrFactory<ui::ViewAndroid>( 725 view_weak_factory_.reset(new base::WeakPtrFactory<ui::ViewAndroid>(
689 web_contents()->GetNativeView())); 726 web_contents()->GetNativeView()));
(...skipping 16 matching lines...) Expand all
706 case base::TERMINATION_STATUS_OOM_PROTECTED: 743 case base::TERMINATION_STATUS_OOM_PROTECTED:
707 #endif 744 #endif
708 case base::TERMINATION_STATUS_LAUNCH_FAILED: 745 case base::TERMINATION_STATUS_LAUNCH_FAILED:
709 inspector_handler_->TargetCrashed(); 746 inspector_handler_->TargetCrashed();
710 current_frame_crashed_ = true; 747 current_frame_crashed_ = true;
711 break; 748 break;
712 default: 749 default:
713 inspector_handler_->TargetDetached("Render process gone."); 750 inspector_handler_->TargetDetached("Render process gone.");
714 break; 751 break;
715 } 752 }
753 DCHECK(CheckConsistency());
716 } 754 }
717 755
718 bool RenderFrameDevToolsAgentHost::OnMessageReceived( 756 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
719 const IPC::Message& message) { 757 const IPC::Message& message) {
720 if (!current_) 758 if (!current_)
721 return false; 759 return false;
722 if (message.type() == ViewHostMsg_SwapCompositorFrame::ID) 760 if (message.type() == ViewHostMsg_SwapCompositorFrame::ID)
723 OnSwapCompositorFrame(message); 761 OnSwapCompositorFrame(message);
724 return false; 762 return false;
725 } 763 }
(...skipping 17 matching lines...) Expand all
743 IPC_MESSAGE_UNHANDLED(handled = false) 781 IPC_MESSAGE_UNHANDLED(handled = false)
744 IPC_END_MESSAGE_MAP() 782 IPC_END_MESSAGE_MAP()
745 return handled; 783 return handled;
746 } 784 }
747 785
748 void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() { 786 void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() {
749 if (page_handler_) 787 if (page_handler_)
750 page_handler_->DidAttachInterstitialPage(); 788 page_handler_->DidAttachInterstitialPage();
751 789
752 // TODO(dgozman): this may break for cross-process subframes. 790 // TODO(dgozman): this may break for cross-process subframes.
753 if (!pending_) 791 if (!pending_) {
792 DCHECK(CheckConsistency());
754 return; 793 return;
794 }
755 // Pending set in AboutToNavigateRenderFrame turned out to be interstitial. 795 // Pending set in AboutToNavigateRenderFrame turned out to be interstitial.
756 // Connect back to the real one. 796 // Connect back to the real one.
757 DiscardPending(); 797 DiscardPending();
758 pending_handle_ = nullptr; 798 pending_handle_ = nullptr;
799 DCHECK(CheckConsistency());
759 } 800 }
760 801
761 void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() { 802 void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
762 if (page_handler_) 803 if (page_handler_)
763 page_handler_->DidDetachInterstitialPage(); 804 page_handler_->DidDetachInterstitialPage();
764 } 805 }
765 806
766 void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame( 807 void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame(
767 RenderFrameHost* render_frame_host, 808 RenderFrameHost* render_frame_host,
768 const GURL& url, 809 const GURL& url,
769 ui::PageTransition transition_type) { 810 ui::PageTransition transition_type) {
770 if (IsBrowserSideNavigationEnabled()) 811 if (IsBrowserSideNavigationEnabled())
771 return; 812 return;
772 if (pending_ && pending_->host() == render_frame_host) 813 if (pending_ && pending_->host() == render_frame_host)
773 CommitPending(); 814 CommitPending();
815 DCHECK(CheckConsistency());
774 service_worker_handler_->UpdateHosts(); 816 service_worker_handler_->UpdateHosts();
775 } 817 }
776 818
777 void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad( 819 void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad(
778 RenderFrameHost* render_frame_host, 820 RenderFrameHost* render_frame_host,
779 const GURL& validated_url, 821 const GURL& validated_url,
780 int error_code, 822 int error_code,
781 const base::string16& error_description, 823 const base::string16& error_description,
782 bool was_ignored_by_handler) { 824 bool was_ignored_by_handler) {
783 if (IsBrowserSideNavigationEnabled()) 825 if (IsBrowserSideNavigationEnabled())
784 return; 826 return;
785 if (pending_ && pending_->host() == render_frame_host) 827 if (pending_ && pending_->host() == render_frame_host)
786 DiscardPending(); 828 DiscardPending();
829 DCHECK(CheckConsistency());
787 } 830 }
788 831
789 void RenderFrameDevToolsAgentHost::WebContentsDestroyed() { 832 void RenderFrameDevToolsAgentHost::WebContentsDestroyed() {
790 #if defined(OS_ANDROID) 833 #if defined(OS_ANDROID)
791 view_weak_factory_.reset(); 834 view_weak_factory_.reset();
792 #endif 835 #endif
793 } 836 }
794 837
795 void RenderFrameDevToolsAgentHost::WasShown() { 838 void RenderFrameDevToolsAgentHost::WasShown() {
796 CreatePowerSaveBlocker(); 839 CreatePowerSaveBlocker();
(...skipping 14 matching lines...) Expand all
811 current_->DispatchProtocolMessage( 854 current_->DispatchProtocolMessage(
812 pair.second.session_id, pair.first, pair.second.method, 855 pair.second.session_id, pair.first, pair.second.method,
813 pair.second.message); 856 pair.second.message);
814 } 857 }
815 in_navigation_protocol_message_buffer_.clear(); 858 in_navigation_protocol_message_buffer_.clear();
816 } 859 }
817 } 860 }
818 861
819 void RenderFrameDevToolsAgentHost::UpdateProtocolHandlers( 862 void RenderFrameDevToolsAgentHost::UpdateProtocolHandlers(
820 RenderFrameHostImpl* host) { 863 RenderFrameHostImpl* host) {
864 handlers_frame_host_ = host;
821 dom_handler_->SetRenderFrameHost(host); 865 dom_handler_->SetRenderFrameHost(host);
822 if (emulation_handler_) 866 if (emulation_handler_)
823 emulation_handler_->SetRenderFrameHost(host); 867 emulation_handler_->SetRenderFrameHost(host);
824 input_handler_->SetRenderWidgetHost( 868 input_handler_->SetRenderWidgetHost(
825 host ? host->GetRenderWidgetHost() : nullptr); 869 host ? host->GetRenderWidgetHost() : nullptr);
826 inspector_handler_->SetRenderFrameHost(host); 870 inspector_handler_->SetRenderFrameHost(host);
827 network_handler_->SetRenderFrameHost(host); 871 network_handler_->SetRenderFrameHost(host);
828 if (page_handler_) 872 if (page_handler_)
829 page_handler_->SetRenderFrameHost(host); 873 page_handler_->SetRenderFrameHost(host);
830 service_worker_handler_->SetRenderFrameHost(host); 874 service_worker_handler_->SetRenderFrameHost(host);
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
967 RenderFrameHost* host) { 1011 RenderFrameHost* host) {
968 return (current_ && current_->host() == host) || 1012 return (current_ && current_->host() == host) ||
969 (pending_ && pending_->host() == host); 1013 (pending_ && pending_->host() == host);
970 } 1014 }
971 1015
972 bool RenderFrameDevToolsAgentHost::IsChildFrame() { 1016 bool RenderFrameDevToolsAgentHost::IsChildFrame() {
973 return current_ && current_->host()->GetParent(); 1017 return current_ && current_->host()->GetParent();
974 } 1018 }
975 1019
976 } // namespace content 1020 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/devtools/render_frame_devtools_agent_host.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698