OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/frame_host/render_frame_host_manager.h" | 5 #include "content/browser/frame_host/render_frame_host_manager.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 int32_t max_page_id = | 353 int32_t max_page_id = |
354 contents()->GetMaxPageIDForSiteInstance(active_rfh->GetSiteInstance()); | 354 contents()->GetMaxPageIDForSiteInstance(active_rfh->GetSiteInstance()); |
355 | 355 |
356 // Use an observer to avoid accessing a deleted renderer later on when the | 356 // Use an observer to avoid accessing a deleted renderer later on when the |
357 // state is being checked. | 357 // state is being checked. |
358 RenderFrameDeletedObserver rfh_observer(old_rfh); | 358 RenderFrameDeletedObserver rfh_observer(old_rfh); |
359 RenderViewHostDeletedObserver rvh_observer(old_rfh->GetRenderViewHost()); | 359 RenderViewHostDeletedObserver rvh_observer(old_rfh->GetRenderViewHost()); |
360 active_rfh->SendNavigate(max_page_id + 1, entry_id, true, url); | 360 active_rfh->SendNavigate(max_page_id + 1, entry_id, true, url); |
361 | 361 |
362 // Make sure that we start to run the unload handler at the time of commit. | 362 // Make sure that we start to run the unload handler at the time of commit. |
363 bool expecting_rfh_shutdown = false; | |
364 if (old_rfh != active_rfh && !rfh_observer.deleted()) { | 363 if (old_rfh != active_rfh && !rfh_observer.deleted()) { |
365 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, | 364 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, |
366 old_rfh->rfh_state()); | 365 old_rfh->rfh_state()); |
367 if (!old_rfh->GetSiteInstance()->active_frame_count() || | 366 EXPECT_TRUE( |
368 SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
369 expecting_rfh_shutdown = true; | |
370 EXPECT_TRUE( | |
371 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion( | 367 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion( |
372 old_rfh)); | 368 old_rfh)); |
373 } | |
374 } | 369 } |
375 | 370 |
376 // Simulate the swap out ACK coming from the pending renderer. This should | 371 // Simulate the swap out ACK coming from the pending renderer. This should |
377 // either shut down the old RFH or leave it in a swapped out state. | 372 // either shut down the old RFH or leave it in a swapped out state. |
378 if (old_rfh != active_rfh) { | 373 if (old_rfh != active_rfh) { |
379 old_rfh->OnSwappedOut(); | 374 old_rfh->OnSwappedOut(); |
380 if (expecting_rfh_shutdown) { | 375 EXPECT_TRUE(rfh_observer.deleted()); |
381 EXPECT_TRUE(rfh_observer.deleted()); | |
382 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
383 EXPECT_TRUE(rvh_observer.deleted()); | |
384 } | |
385 } else { | |
386 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT, | |
387 old_rfh->rfh_state()); | |
388 } | |
389 } | 376 } |
390 EXPECT_EQ(active_rfh, contents()->GetMainFrame()); | 377 EXPECT_EQ(active_rfh, contents()->GetMainFrame()); |
391 EXPECT_EQ(NULL, contents()->GetPendingMainFrame()); | 378 EXPECT_EQ(NULL, contents()->GetPendingMainFrame()); |
392 } | 379 } |
393 | 380 |
394 bool ShouldSwapProcesses(RenderFrameHostManager* manager, | 381 bool ShouldSwapProcesses(RenderFrameHostManager* manager, |
395 const NavigationEntryImpl* current_entry, | 382 const NavigationEntryImpl* current_entry, |
396 const NavigationEntryImpl* new_entry) const { | 383 const NavigationEntryImpl* new_entry) const { |
397 CHECK(new_entry); | 384 CHECK(new_entry); |
398 BrowserContext* browser_context = | 385 BrowserContext* browser_context = |
399 manager->delegate_->GetControllerForRenderManager().GetBrowserContext(); | 386 manager->delegate_->GetControllerForRenderManager().GetBrowserContext(); |
400 const GURL& current_effective_url = current_entry ? | 387 const GURL& current_effective_url = current_entry ? |
401 SiteInstanceImpl::GetEffectiveURL(browser_context, | 388 SiteInstanceImpl::GetEffectiveURL(browser_context, |
402 current_entry->GetURL()) : | 389 current_entry->GetURL()) : |
403 manager->render_frame_host_->GetSiteInstance()->GetSiteURL(); | 390 manager->render_frame_host_->GetSiteInstance()->GetSiteURL(); |
404 bool current_is_view_source_mode = current_entry ? | 391 bool current_is_view_source_mode = current_entry ? |
405 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode(); | 392 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode(); |
406 return manager->ShouldSwapBrowsingInstancesForNavigation( | 393 return manager->ShouldSwapBrowsingInstancesForNavigation( |
407 current_effective_url, | 394 current_effective_url, |
408 current_is_view_source_mode, | 395 current_is_view_source_mode, |
409 new_entry->site_instance(), | 396 new_entry->site_instance(), |
410 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()), | 397 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()), |
411 new_entry->IsViewSourceMode()); | 398 new_entry->IsViewSourceMode()); |
412 } | 399 } |
413 | 400 |
414 // Creates a test RenderFrameHost that's swapped out. | 401 // Creates a test RenderViewHost that's swapped out. |
415 TestRenderFrameHost* CreateSwappedOutRenderFrameHost() { | 402 void CreateSwappedOutRenderViewHost() { |
416 const GURL kChromeURL("chrome://foo"); | 403 const GURL kChromeURL("chrome://foo"); |
417 const GURL kDestUrl("http://www.google.com/"); | 404 const GURL kDestUrl("http://www.google.com/"); |
418 | 405 |
419 // Navigate our first tab to a chrome url and then to the destination. | 406 // Navigate our first tab to a chrome url and then to the destination. |
420 NavigateActiveAndCommit(kChromeURL); | 407 NavigateActiveAndCommit(kChromeURL); |
421 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); | 408 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); |
422 | 409 |
423 // Navigate to a cross-site URL. | 410 // Navigate to a cross-site URL. |
424 contents()->GetController().LoadURL( | 411 contents()->GetController().LoadURL( |
425 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); | 412 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); |
426 int entry_id = contents()->GetController().GetPendingEntry()->GetUniqueID(); | 413 int entry_id = contents()->GetController().GetPendingEntry()->GetUniqueID(); |
427 contents()->GetMainFrame()->PrepareForCommit(); | 414 contents()->GetMainFrame()->PrepareForCommit(); |
428 EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 415 EXPECT_TRUE(contents()->CrossProcessNavigationPending()); |
429 | 416 |
430 // Manually increase the number of active frames in the | 417 // Manually increase the number of active frames in the |
431 // SiteInstance that ntp_rfh belongs to, to prevent it from being | 418 // SiteInstance that ntp_rfh belongs to, to prevent it from being |
432 // destroyed when it gets swapped out. | 419 // destroyed when it gets swapped out. |
433 ntp_rfh->GetSiteInstance()->IncrementActiveFrameCount(); | 420 ntp_rfh->GetSiteInstance()->IncrementActiveFrameCount(); |
434 | 421 |
435 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame(); | 422 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame(); |
436 CHECK(dest_rfh); | 423 CHECK(dest_rfh); |
437 EXPECT_NE(ntp_rfh, dest_rfh); | 424 EXPECT_NE(ntp_rfh, dest_rfh); |
438 | 425 |
439 // BeforeUnload finishes. | 426 // BeforeUnload finishes. |
440 ntp_rfh->SendBeforeUnloadACK(true); | 427 ntp_rfh->SendBeforeUnloadACK(true); |
441 | 428 |
442 dest_rfh->SendNavigate(101, entry_id, true, kDestUrl); | 429 dest_rfh->SendNavigate(101, entry_id, true, kDestUrl); |
443 ntp_rfh->OnSwappedOut(); | 430 ntp_rfh->OnSwappedOut(); |
444 | |
445 EXPECT_TRUE(ntp_rfh->is_swapped_out()); | |
446 return ntp_rfh; | |
447 } | 431 } |
448 | 432 |
449 // Returns the RenderFrameHost that should be used in the navigation to | 433 // Returns the RenderFrameHost that should be used in the navigation to |
450 // |entry|. | 434 // |entry|. |
451 RenderFrameHostImpl* NavigateToEntry( | 435 RenderFrameHostImpl* NavigateToEntry( |
452 RenderFrameHostManager* manager, | 436 RenderFrameHostManager* manager, |
453 const NavigationEntryImpl& entry) { | 437 const NavigationEntryImpl& entry) { |
454 // Tests currently only navigate using main frame FrameNavigationEntries. | 438 // Tests currently only navigate using main frame FrameNavigationEntries. |
455 FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get(); | 439 FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get(); |
456 if (IsBrowserSideNavigationEnabled()) { | 440 if (IsBrowserSideNavigationEnabled()) { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 | 620 |
637 // The old renderer, being slow, now updates the favicon. It should be | 621 // The old renderer, being slow, now updates the favicon. It should be |
638 // filtered out and not take effect. | 622 // filtered out and not take effect. |
639 { | 623 { |
640 PluginFaviconMessageObserver observer(contents()); | 624 PluginFaviconMessageObserver observer(contents()); |
641 EXPECT_TRUE( | 625 EXPECT_TRUE( |
642 ntp_rvh->GetWidget()->OnMessageReceived(ViewHostMsg_UpdateFaviconURL( | 626 ntp_rvh->GetWidget()->OnMessageReceived(ViewHostMsg_UpdateFaviconURL( |
643 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons))); | 627 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons))); |
644 EXPECT_FALSE(observer.favicon_received()); | 628 EXPECT_FALSE(observer.favicon_received()); |
645 } | 629 } |
646 | |
647 // In --site-per-process, the RenderFrameHost is deleted on cross-process | |
648 // navigation, so the rest of the test case doesn't apply. | |
649 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
650 return; | |
651 } | |
652 | |
653 #if defined(ENABLE_PLUGINS) | |
654 // The same logic should apply to RenderFrameHosts as well and routing through | |
655 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check | |
656 // if the IPC message is allowed through or not. | |
657 { | |
658 PluginFaviconMessageObserver observer(contents()); | |
659 EXPECT_TRUE(ntp_rfh->OnMessageReceived( | |
660 FrameHostMsg_PluginCrashed( | |
661 ntp_rfh->GetRoutingID(), base::FilePath(), 0))); | |
662 EXPECT_FALSE(observer.plugin_crashed()); | |
663 } | |
664 #endif | |
665 | |
666 // We cannot filter out synchronous IPC messages, because the renderer would | |
667 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example | |
668 // that can run easily within a unit test, and that needs to receive a reply | |
669 // without showing an actual dialog. | |
670 MockRenderProcessHost* ntp_process_host = ntp_rfh->GetProcess(); | |
671 ntp_process_host->sink().ClearMessages(); | |
672 bool result = false; | |
673 base::string16 unused; | |
674 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg( | |
675 ntp_rfh->GetRoutingID(), kChromeURL, false, &result, &unused); | |
676 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg)); | |
677 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); | |
678 | |
679 // Also test RunJavaScriptMessage. | |
680 ntp_process_host->sink().ClearMessages(); | |
681 const base::string16 msg = base::ASCIIToUTF16("Message"); | |
682 FrameHostMsg_RunJavaScriptMessage js_msg( | |
683 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL, | |
684 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused); | |
685 js_msg.EnableMessagePumping(); | |
686 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg)); | |
687 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); | |
688 } | 630 } |
689 | 631 |
690 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the | 632 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the |
691 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes | 633 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes |
692 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently | 634 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently |
693 // committed navigation for each WebContentsImpl. | 635 // committed navigation for each WebContentsImpl. |
694 TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) { | 636 TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) { |
695 const GURL kChromeURL("chrome://foo"); | 637 const GURL kChromeURL("chrome://foo"); |
696 const GURL kDestUrl("http://www.google.com/"); | 638 const GURL kDestUrl("http://www.google.com/"); |
697 std::vector<FaviconURL> icons; | 639 std::vector<FaviconURL> icons; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
737 // be ignored. | 679 // be ignored. |
738 { | 680 { |
739 PluginFaviconMessageObserver observer(contents()); | 681 PluginFaviconMessageObserver observer(contents()); |
740 EXPECT_TRUE(rfh1->GetRenderViewHost()->GetWidget()->OnMessageReceived( | 682 EXPECT_TRUE(rfh1->GetRenderViewHost()->GetWidget()->OnMessageReceived( |
741 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(), | 683 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(), |
742 icons))); | 684 icons))); |
743 EXPECT_FALSE(observer.favicon_received()); | 685 EXPECT_FALSE(observer.favicon_received()); |
744 } | 686 } |
745 } | 687 } |
746 | 688 |
747 // Ensure that frames aren't added to the frame tree, if the message is coming | |
748 // from a process different than the parent frame's current RenderFrameHost | |
749 // process. Otherwise it is possible to have collisions of routing ids, as they | |
750 // are scoped per process. See https://crbug.com/415059. | |
751 TEST_F(RenderFrameHostManagerTest, DropCreateChildFrameWhileSwappedOut) { | |
752 const GURL kUrl1("http://foo.com"); | |
753 const GURL kUrl2("http://www.google.com/"); | |
754 | |
755 // This test is invalid in --site-per-process mode, as swapped-out is no | |
756 // longer used. | |
757 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
758 return; | |
759 } | |
760 | |
761 // Navigate to the first site. | |
762 NavigateActiveAndCommit(kUrl1); | |
763 TestRenderFrameHost* initial_rfh = contents()->GetMainFrame(); | |
764 { | |
765 RenderFrameHostCreatedObserver observer(contents()); | |
766 initial_rfh->OnCreateChildFrame( | |
767 initial_rfh->GetProcess()->GetNextRoutingID(), | |
768 blink::WebTreeScopeType::Document, std::string(), "uniqueName1", | |
769 blink::WebSandboxFlags::None, blink::WebFrameOwnerProperties()); | |
770 EXPECT_TRUE(observer.created()); | |
771 } | |
772 | |
773 // Create one more frame in the same SiteInstance where initial_rfh | |
774 // exists so that initial_rfh doesn't get deleted on navigation to another | |
775 // site. | |
776 initial_rfh->GetSiteInstance()->IncrementActiveFrameCount(); | |
777 | |
778 // Navigate to a cross-site URL. | |
779 NavigateActiveAndCommit(kUrl2); | |
780 EXPECT_TRUE(initial_rfh->is_swapped_out()); | |
781 | |
782 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame(); | |
783 ASSERT_TRUE(dest_rfh); | |
784 EXPECT_NE(initial_rfh, dest_rfh); | |
785 | |
786 { | |
787 // Since the old RFH is now swapped out, it shouldn't process any messages | |
788 // to create child frames. | |
789 RenderFrameHostCreatedObserver observer(contents()); | |
790 initial_rfh->OnCreateChildFrame( | |
791 initial_rfh->GetProcess()->GetNextRoutingID(), | |
792 blink::WebTreeScopeType::Document, std::string(), "uniqueName2", | |
793 blink::WebSandboxFlags::None, blink::WebFrameOwnerProperties()); | |
794 EXPECT_FALSE(observer.created()); | |
795 } | |
796 } | |
797 | |
798 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) { | |
799 // TODO(nasko): Check with kenrb whether this test can be rewritten and | |
800 // whether it makes sense when swapped out is replaced with proxies. | |
801 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
802 return; | |
803 } | |
804 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost(); | |
805 TestRenderWidgetHostView* swapped_out_rwhv = | |
806 static_cast<TestRenderWidgetHostView*>( | |
807 swapped_out_rfh->GetRenderViewHost()->GetWidget()->GetView()); | |
808 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame()); | |
809 | |
810 MockRenderProcessHost* process_host = swapped_out_rfh->GetProcess(); | |
811 process_host->sink().ClearMessages(); | |
812 | |
813 cc::CompositorFrame frame; | |
814 ViewHostMsg_SwapCompositorFrame msg( | |
815 rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>()); | |
816 | |
817 EXPECT_TRUE( | |
818 swapped_out_rfh->render_view_host()->GetWidget()->OnMessageReceived(msg)); | |
819 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame()); | |
820 } | |
821 | |
822 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active | 689 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active |
823 // widgets. | 690 // widgets. |
824 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { | 691 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { |
825 // This test is invalid in --site-per-process mode, as swapped-out is no | 692 CreateSwappedOutRenderViewHost(); |
826 // longer used. | |
827 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
828 return; | |
829 } | |
830 | |
831 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost(); | |
832 EXPECT_TRUE(swapped_out_rfh->is_swapped_out()); | |
833 | |
834 scoped_ptr<RenderWidgetHostIterator> widgets( | 693 scoped_ptr<RenderWidgetHostIterator> widgets( |
835 RenderWidgetHost::GetRenderWidgetHosts()); | 694 RenderWidgetHost::GetRenderWidgetHosts()); |
| 695 |
836 // We know that there is the only one active widget. Another view is | 696 // We know that there is the only one active widget. Another view is |
837 // now swapped out, so the swapped out view is not included in the | 697 // now swapped out, so the swapped out view is not included in the |
838 // list. | 698 // list. |
839 RenderWidgetHost* widget = widgets->GetNextHost(); | 699 RenderWidgetHost* widget = widgets->GetNextHost(); |
840 EXPECT_FALSE(widgets->GetNextHost()); | 700 EXPECT_FALSE(widgets->GetNextHost()); |
841 RenderViewHost* rvh = RenderViewHost::From(widget); | 701 RenderViewHost* rvh = RenderViewHost::From(widget); |
842 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(rvh)->is_active()); | 702 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(rvh)->is_active()); |
843 } | 703 } |
844 | 704 |
845 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of | 705 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of |
846 // RenderViewHostImpl::GetAllRenderWidgetHosts(). | 706 // RenderViewHostImpl::GetAllRenderWidgetHosts(). |
847 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but | 707 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but |
848 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything | 708 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything |
849 // including swapped out ones. | 709 // including swapped out ones. |
850 TEST_F(RenderFrameHostManagerTest, | 710 TEST_F(RenderFrameHostManagerTest, |
851 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) { | 711 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) { |
852 // This test is invalid in --site-per-process mode, as swapped-out is no | 712 CreateSwappedOutRenderViewHost(); |
853 // longer used. | |
854 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
855 return; | |
856 } | |
857 | |
858 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost(); | |
859 EXPECT_TRUE(swapped_out_rfh->is_swapped_out()); | |
860 | |
861 scoped_ptr<RenderWidgetHostIterator> widgets( | 713 scoped_ptr<RenderWidgetHostIterator> widgets( |
862 RenderWidgetHost::GetRenderWidgetHosts()); | 714 RenderWidgetHost::GetRenderWidgetHosts()); |
863 | 715 |
864 while (RenderWidgetHost* w = widgets->GetNextHost()) { | 716 while (RenderWidgetHost* w = widgets->GetNextHost()) { |
865 bool found = false; | 717 bool found = false; |
866 scoped_ptr<RenderWidgetHostIterator> all_widgets( | 718 scoped_ptr<RenderWidgetHostIterator> all_widgets( |
867 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); | 719 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); |
868 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) { | 720 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) { |
869 if (w == widget) { | 721 if (w == widget) { |
870 found = true; | 722 found = true; |
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1436 EXPECT_TRUE(rfh2->IsWaitingForUnloadACK()); | 1288 EXPECT_TRUE(rfh2->IsWaitingForUnloadACK()); |
1437 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh2->rfh_state()); | 1289 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh2->rfh_state()); |
1438 | 1290 |
1439 // We should be able to navigate forward. | 1291 // We should be able to navigate forward. |
1440 contents()->GetController().GoForward(); | 1292 contents()->GetController().GoForward(); |
1441 contents()->GetMainFrame()->PrepareForCommit(); | 1293 contents()->GetMainFrame()->PrepareForCommit(); |
1442 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry(); | 1294 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry(); |
1443 contents()->GetPendingMainFrame()->SendNavigate( | 1295 contents()->GetPendingMainFrame()->SendNavigate( |
1444 entry2->GetPageID(), entry2->GetUniqueID(), false, entry2->GetURL()); | 1296 entry2->GetPageID(), entry2->GetUniqueID(), false, entry2->GetURL()); |
1445 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state()); | 1297 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state()); |
1446 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | |
1447 EXPECT_EQ(rfh2, main_test_rfh()); | |
1448 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); | |
1449 rfh1->OnSwappedOut(); | |
1450 EXPECT_TRUE(rfh1->is_swapped_out()); | |
1451 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT, rfh1->rfh_state()); | |
1452 } | |
1453 } | 1298 } |
1454 | 1299 |
1455 // Test that we create swapped out RFHs for the opener chain when navigating an | 1300 // Test that we create swapped out RFHs for the opener chain when navigating an |
1456 // opened tab cross-process. This allows us to support certain cross-process | 1301 // opened tab cross-process. This allows us to support certain cross-process |
1457 // JavaScript calls (http://crbug.com/99202). | 1302 // JavaScript calls (http://crbug.com/99202). |
1458 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) { | 1303 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) { |
1459 const GURL kUrl1("http://www.google.com/"); | 1304 const GURL kUrl1("http://www.google.com/"); |
1460 const GURL kUrl2("http://www.chromium.org/"); | 1305 const GURL kUrl2("http://www.chromium.org/"); |
1461 const GURL kChromeUrl("chrome://foo"); | 1306 const GURL kChromeUrl("chrome://foo"); |
1462 | 1307 |
(...skipping 21 matching lines...) Expand all Loading... |
1484 | 1329 |
1485 // Navigate to a cross-site URL (different SiteInstance but same | 1330 // Navigate to a cross-site URL (different SiteInstance but same |
1486 // BrowsingInstance). | 1331 // BrowsingInstance). |
1487 contents()->NavigateAndCommit(kUrl2); | 1332 contents()->NavigateAndCommit(kUrl2); |
1488 TestRenderFrameHost* rfh2 = main_test_rfh(); | 1333 TestRenderFrameHost* rfh2 = main_test_rfh(); |
1489 TestRenderViewHost* rvh2 = test_rvh(); | 1334 TestRenderViewHost* rvh2 = test_rvh(); |
1490 EXPECT_NE(site_instance1, rfh2->GetSiteInstance()); | 1335 EXPECT_NE(site_instance1, rfh2->GetSiteInstance()); |
1491 EXPECT_TRUE(site_instance1->IsRelatedSiteInstance(rfh2->GetSiteInstance())); | 1336 EXPECT_TRUE(site_instance1->IsRelatedSiteInstance(rfh2->GetSiteInstance())); |
1492 | 1337 |
1493 // Ensure rvh1 is placed on swapped out list of the current tab. | 1338 // Ensure rvh1 is placed on swapped out list of the current tab. |
1494 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1339 EXPECT_TRUE(rfh1_deleted_observer.deleted()); |
1495 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1)); | 1340 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get())); |
1496 EXPECT_FALSE(rfh1_deleted_observer.deleted()); | |
1497 EXPECT_TRUE(manager->IsOnSwappedOutList(rfh1)); | |
1498 EXPECT_EQ(rfh1, | |
1499 manager->GetRenderFrameProxyHost(site_instance1.get()) | |
1500 ->render_frame_host()); | |
1501 } else { | |
1502 EXPECT_TRUE(rfh1_deleted_observer.deleted()); | |
1503 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get())); | |
1504 } | |
1505 EXPECT_EQ(rvh1, | 1341 EXPECT_EQ(rvh1, |
1506 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance())); | 1342 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance())); |
1507 | 1343 |
1508 // Ensure a swapped out RFH and RFH is created in the first opener tab. | 1344 // Ensure a swapped out RFH and RFH is created in the first opener tab. |
1509 RenderFrameProxyHost* opener1_proxy = | 1345 RenderFrameProxyHost* opener1_proxy = |
1510 opener1_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance()); | 1346 opener1_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance()); |
1511 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host(); | 1347 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host(); |
1512 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( | 1348 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( |
1513 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | 1349 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); |
1514 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1350 EXPECT_FALSE(opener1_rfh); |
1515 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh)); | |
1516 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); | |
1517 EXPECT_TRUE(opener1_rfh->is_swapped_out()); | |
1518 } else { | |
1519 EXPECT_FALSE(opener1_rfh); | |
1520 } | |
1521 EXPECT_FALSE(opener1_rvh->is_active()); | 1351 EXPECT_FALSE(opener1_rvh->is_active()); |
1522 | 1352 |
1523 // Ensure a swapped out RFH and RVH is created in the second opener tab. | 1353 // Ensure a swapped out RFH and RVH is created in the second opener tab. |
1524 RenderFrameProxyHost* opener2_proxy = | 1354 RenderFrameProxyHost* opener2_proxy = |
1525 opener2_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance()); | 1355 opener2_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance()); |
1526 RenderFrameHostImpl* opener2_rfh = opener2_proxy->render_frame_host(); | 1356 RenderFrameHostImpl* opener2_rfh = opener2_proxy->render_frame_host(); |
1527 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( | 1357 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( |
1528 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | 1358 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); |
1529 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1359 EXPECT_FALSE(opener2_rfh); |
1530 EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rfh)); | |
1531 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh)); | |
1532 EXPECT_TRUE(opener2_rfh->is_swapped_out()); | |
1533 } else { | |
1534 EXPECT_FALSE(opener2_rfh); | |
1535 } | |
1536 EXPECT_FALSE(opener2_rvh->is_active()); | 1360 EXPECT_FALSE(opener2_rvh->is_active()); |
1537 | 1361 |
1538 // Navigate to a cross-BrowsingInstance URL. | 1362 // Navigate to a cross-BrowsingInstance URL. |
1539 contents()->NavigateAndCommit(kChromeUrl); | 1363 contents()->NavigateAndCommit(kChromeUrl); |
1540 TestRenderFrameHost* rfh3 = main_test_rfh(); | 1364 TestRenderFrameHost* rfh3 = main_test_rfh(); |
1541 EXPECT_NE(site_instance1, rfh3->GetSiteInstance()); | 1365 EXPECT_NE(site_instance1, rfh3->GetSiteInstance()); |
1542 EXPECT_FALSE(site_instance1->IsRelatedSiteInstance(rfh3->GetSiteInstance())); | 1366 EXPECT_FALSE(site_instance1->IsRelatedSiteInstance(rfh3->GetSiteInstance())); |
1543 | 1367 |
1544 // No scripting is allowed across BrowsingInstances, so we should not create | 1368 // No scripting is allowed across BrowsingInstances, so we should not create |
1545 // swapped out RVHs for the opener chain in this case. | 1369 // swapped out RVHs for the opener chain in this case. |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1772 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); | 1596 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); |
1773 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( | 1597 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( |
1774 rvh2->GetSiteInstance())); | 1598 rvh2->GetSiteInstance())); |
1775 | 1599 |
1776 // Ensure a swapped out RFH and RVH is created in the first opener tab. | 1600 // Ensure a swapped out RFH and RVH is created in the first opener tab. |
1777 RenderFrameProxyHost* opener1_proxy = | 1601 RenderFrameProxyHost* opener1_proxy = |
1778 opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance()); | 1602 opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance()); |
1779 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host(); | 1603 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host(); |
1780 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( | 1604 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( |
1781 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | 1605 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); |
1782 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1606 EXPECT_FALSE(opener1_rfh); |
1783 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh)); | |
1784 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); | |
1785 EXPECT_TRUE(opener1_rfh->is_swapped_out()); | |
1786 } else { | |
1787 EXPECT_FALSE(opener1_rfh); | |
1788 } | |
1789 EXPECT_FALSE(opener1_rvh->is_active()); | 1607 EXPECT_FALSE(opener1_rvh->is_active()); |
1790 | 1608 |
1791 // Ensure the new RVH has WebUI bindings. | 1609 // Ensure the new RVH has WebUI bindings. |
1792 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | 1610 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); |
1793 } | 1611 } |
1794 | 1612 |
1795 // Test that we reuse the same guest SiteInstance if we navigate across sites. | 1613 // Test that we reuse the same guest SiteInstance if we navigate across sites. |
1796 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) { | 1614 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) { |
1797 GURL guest_url(std::string(kGuestScheme).append("://abc123")); | 1615 GURL guest_url(std::string(kGuestScheme).append("://abc123")); |
1798 SiteInstance* instance = | 1616 SiteInstance* instance = |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2015 ui::PAGE_TRANSITION_TYPED); | 1833 ui::PAGE_TRANSITION_TYPED); |
2016 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 1834 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); |
2017 EXPECT_EQ(rfh2, contents()->GetMainFrame()); | 1835 EXPECT_EQ(rfh2, contents()->GetMainFrame()); |
2018 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); | 1836 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); |
2019 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state()); | 1837 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state()); |
2020 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); | 1838 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); |
2021 | 1839 |
2022 // Simulate the swap out ack. | 1840 // Simulate the swap out ack. |
2023 rfh1->OnSwappedOut(); | 1841 rfh1->OnSwappedOut(); |
2024 | 1842 |
2025 // rfh1 should be swapped out or deleted in --site-per-process. | 1843 // rfh1 should be deleted. |
2026 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1844 EXPECT_TRUE(rfh_deleted_observer.deleted()); |
2027 EXPECT_FALSE(rfh_deleted_observer.deleted()); | |
2028 EXPECT_TRUE(rfh1->is_swapped_out()); | |
2029 } else { | |
2030 EXPECT_TRUE(rfh_deleted_observer.deleted()); | |
2031 } | |
2032 } | 1845 } |
2033 | 1846 |
2034 // Test that the RenderViewHost is properly swapped out if a navigation in the | 1847 // Test that the RenderViewHost is properly swapped out if a navigation in the |
2035 // new renderer commits before sending the SwapOut message to the old renderer. | 1848 // new renderer commits before sending the SwapOut message to the old renderer. |
2036 // This simulates a cross-site navigation to a synchronously committing URL | 1849 // This simulates a cross-site navigation to a synchronously committing URL |
2037 // (e.g., a data URL) and ensures it works properly. | 1850 // (e.g., a data URL) and ensures it works properly. |
2038 TEST_F(RenderFrameHostManagerTest, | 1851 TEST_F(RenderFrameHostManagerTest, |
2039 CommitNewNavigationBeforeSendingSwapOut) { | 1852 CommitNewNavigationBeforeSendingSwapOut) { |
2040 const GURL kUrl1("http://www.google.com/"); | 1853 const GURL kUrl1("http://www.google.com/"); |
2041 const GURL kUrl2("http://www.chromium.org/"); | 1854 const GURL kUrl2("http://www.chromium.org/"); |
(...skipping 22 matching lines...) Expand all Loading... |
2064 ui::PAGE_TRANSITION_TYPED); | 1877 ui::PAGE_TRANSITION_TYPED); |
2065 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 1878 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); |
2066 EXPECT_EQ(rfh2, contents()->GetMainFrame()); | 1879 EXPECT_EQ(rfh2, contents()->GetMainFrame()); |
2067 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); | 1880 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); |
2068 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state()); | 1881 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state()); |
2069 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); | 1882 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); |
2070 | 1883 |
2071 // Simulate the swap out ack. | 1884 // Simulate the swap out ack. |
2072 rfh1->OnSwappedOut(); | 1885 rfh1->OnSwappedOut(); |
2073 | 1886 |
2074 // rfh1 should be swapped out. | 1887 // rfh1 should be deleted. |
2075 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1888 EXPECT_TRUE(rfh_deleted_observer.deleted()); |
2076 EXPECT_TRUE(rfh_deleted_observer.deleted()); | 1889 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager() |
2077 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager() | 1890 ->GetRenderFrameProxyHost(site_instance.get())); |
2078 ->GetRenderFrameProxyHost(site_instance.get())); | |
2079 } else { | |
2080 EXPECT_FALSE(rfh_deleted_observer.deleted()); | |
2081 EXPECT_TRUE(rfh1->is_swapped_out()); | |
2082 } | |
2083 } | 1891 } |
2084 | 1892 |
2085 // Test that a RenderFrameHost is properly deleted when a cross-site navigation | 1893 // Test that a RenderFrameHost is properly deleted when a cross-site navigation |
2086 // is cancelled. | 1894 // is cancelled. |
2087 TEST_F(RenderFrameHostManagerTest, | 1895 TEST_F(RenderFrameHostManagerTest, |
2088 CancelPendingProperlyDeletesOrSwaps) { | 1896 CancelPendingProperlyDeletesOrSwaps) { |
2089 const GURL kUrl1("http://www.google.com/"); | 1897 const GURL kUrl1("http://www.google.com/"); |
2090 const GURL kUrl2("http://www.chromium.org/"); | 1898 const GURL kUrl2("http://www.chromium.org/"); |
2091 RenderFrameHostImpl* pending_rfh = NULL; | 1899 RenderFrameHostImpl* pending_rfh = NULL; |
2092 base::TimeTicks now = base::TimeTicks::Now(); | 1900 base::TimeTicks now = base::TimeTicks::Now(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2124 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be | 1932 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be |
2125 // created. | 1933 // created. |
2126 scoped_refptr<SiteInstanceImpl> site_instance = | 1934 scoped_refptr<SiteInstanceImpl> site_instance = |
2127 pending_rfh->GetSiteInstance(); | 1935 pending_rfh->GetSiteInstance(); |
2128 site_instance->IncrementActiveFrameCount(); | 1936 site_instance->IncrementActiveFrameCount(); |
2129 | 1937 |
2130 contents()->GetMainFrame()->OnMessageReceived( | 1938 contents()->GetMainFrame()->OnMessageReceived( |
2131 FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); | 1939 FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); |
2132 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 1940 EXPECT_FALSE(contents()->CrossProcessNavigationPending()); |
2133 | 1941 |
2134 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { | 1942 EXPECT_TRUE(rfh_deleted_observer.deleted()); |
2135 EXPECT_TRUE(rfh_deleted_observer.deleted()); | 1943 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager() |
2136 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager() | 1944 ->GetRenderFrameProxyHost(site_instance.get())); |
2137 ->GetRenderFrameProxyHost(site_instance.get())); | |
2138 } else { | |
2139 EXPECT_FALSE(rfh_deleted_observer.deleted()); | |
2140 } | |
2141 } | 1945 } |
2142 } | 1946 } |
2143 | 1947 |
2144 class RenderFrameHostManagerTestWithSiteIsolation | 1948 class RenderFrameHostManagerTestWithSiteIsolation |
2145 : public RenderFrameHostManagerTest { | 1949 : public RenderFrameHostManagerTest { |
2146 public: | 1950 public: |
2147 RenderFrameHostManagerTestWithSiteIsolation() { | 1951 RenderFrameHostManagerTestWithSiteIsolation() { |
2148 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); | 1952 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); |
2149 } | 1953 } |
2150 }; | 1954 }; |
(...skipping 1152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3303 commit_params.should_enforce_strict_mixed_content_checking = false; | 3107 commit_params.should_enforce_strict_mixed_content_checking = false; |
3304 child_host->SendNavigateWithParams(&commit_params); | 3108 child_host->SendNavigateWithParams(&commit_params); |
3305 EXPECT_NO_FATAL_FAILURE(CheckMixedContentIPC( | 3109 EXPECT_NO_FATAL_FAILURE(CheckMixedContentIPC( |
3306 main_test_rfh(), false, proxy_to_parent->GetRoutingID())); | 3110 main_test_rfh(), false, proxy_to_parent->GetRoutingID())); |
3307 EXPECT_FALSE(root->child_at(0) | 3111 EXPECT_FALSE(root->child_at(0) |
3308 ->current_replication_state() | 3112 ->current_replication_state() |
3309 .should_enforce_strict_mixed_content_checking); | 3113 .should_enforce_strict_mixed_content_checking); |
3310 } | 3114 } |
3311 | 3115 |
3312 } // namespace content | 3116 } // namespace content |
OLD | NEW |