diff options
author | nasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-25 23:01:06 +0000 |
---|---|---|
committer | nasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-25 23:01:06 +0000 |
commit | 893558b0bc3f65c412c552e1c248c68c62df9b1b (patch) | |
tree | b45836fe6949cf7f5c87b85f7df0c6d164f99c9f /content | |
parent | b98c8b5d4fe34cb8a0cf961af55f60673b86e96d (diff) | |
download | chromium_src-893558b0bc3f65c412c552e1c248c68c62df9b1b.zip chromium_src-893558b0bc3f65c412c552e1c248c68c62df9b1b.tar.gz chromium_src-893558b0bc3f65c412c552e1c248c68c62df9b1b.tar.bz2 |
Perform navigation policy check on UI thread for --site-per-process.
This CL adds a NavigateFrameToURL method, which will allow browser tests to navigate specific (sub)frames and more precise out-of-process iframes tests can be written.
It also changes how cross-site transition is done for --site-per-process. It used to depend on the Referer header, but if there is a navigation to data URL, it will be empty and there will always be a transfer to new renderer.
I'm changing it to do the check on the UI thread and for now to be a very simplistic one - is the URL in the same SiteInstance as the current renderer.
BUG=357747
Review URL: https://codereview.chromium.org/248963007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266267 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/frame_host/interstitial_page_navigator_impl.cc | 8 | ||||
-rw-r--r-- | content/browser/frame_host/interstitial_page_navigator_impl.h | 4 | ||||
-rw-r--r-- | content/browser/frame_host/navigator.cc | 4 | ||||
-rw-r--r-- | content/browser/frame_host/navigator.h | 3 | ||||
-rw-r--r-- | content/browser/frame_host/navigator_impl.cc | 4 | ||||
-rw-r--r-- | content/browser/frame_host/navigator_impl.h | 1 | ||||
-rw-r--r-- | content/browser/loader/cross_site_resource_handler.cc | 85 | ||||
-rw-r--r-- | content/browser/loader/cross_site_resource_handler.h | 14 | ||||
-rw-r--r-- | content/browser/site_per_process_browsertest.cc | 39 | ||||
-rw-r--r-- | content/content_tests.gypi | 4 | ||||
-rw-r--r-- | content/test/content_browser_test_utils_internal.cc | 23 | ||||
-rw-r--r-- | content/test/content_browser_test_utils_internal.h | 25 | ||||
-rw-r--r-- | content/test/test_frame_navigation_observer.cc | 80 | ||||
-rw-r--r-- | content/test/test_frame_navigation_observer.h | 75 |
14 files changed, 341 insertions, 28 deletions
diff --git a/content/browser/frame_host/interstitial_page_navigator_impl.cc b/content/browser/frame_host/interstitial_page_navigator_impl.cc index 82f40af..fc45950 100644 --- a/content/browser/frame_host/interstitial_page_navigator_impl.cc +++ b/content/browser/frame_host/interstitial_page_navigator_impl.cc @@ -5,6 +5,7 @@ #include "content/browser/frame_host/interstitial_page_navigator_impl.h" #include "content/browser/frame_host/interstitial_page_impl.h" +#include "content/browser/frame_host/navigation_controller_impl.h" #include "content/browser/frame_host/navigator_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -13,7 +14,12 @@ namespace content { InterstitialPageNavigatorImpl::InterstitialPageNavigatorImpl( InterstitialPageImpl* interstitial, NavigationControllerImpl* navigation_controller) - : interstitial_(interstitial) {} + : interstitial_(interstitial), + controller_(navigation_controller) {} + +NavigationController* InterstitialPageNavigatorImpl::GetController() { + return controller_; +} void InterstitialPageNavigatorImpl::DidNavigate( RenderFrameHostImpl* render_frame_host, diff --git a/content/browser/frame_host/interstitial_page_navigator_impl.h b/content/browser/frame_host/interstitial_page_navigator_impl.h index 2b57f7a..7077546 100644 --- a/content/browser/frame_host/interstitial_page_navigator_impl.h +++ b/content/browser/frame_host/interstitial_page_navigator_impl.h @@ -22,6 +22,7 @@ class CONTENT_EXPORT InterstitialPageNavigatorImpl : public Navigator { InterstitialPageImpl* interstitial, NavigationControllerImpl* navigation_controller); + virtual NavigationController* GetController() OVERRIDE; virtual void DidNavigate( RenderFrameHostImpl* render_frame_host, const FrameHostMsg_DidCommitProvisionalLoad_Params& @@ -34,6 +35,9 @@ class CONTENT_EXPORT InterstitialPageNavigatorImpl : public Navigator { // Non owned pointer. InterstitialPageImpl* interstitial_; + // The NavigationController associated with this navigator. + NavigationControllerImpl* controller_; + DISALLOW_COPY_AND_ASSIGN(InterstitialPageNavigatorImpl); }; diff --git a/content/browser/frame_host/navigator.cc b/content/browser/frame_host/navigator.cc index 6b51a91..3d5eb8f9 100644 --- a/content/browser/frame_host/navigator.cc +++ b/content/browser/frame_host/navigator.cc @@ -8,6 +8,10 @@ namespace content { +NavigationController* Navigator::GetController() { + return NULL; +} + bool Navigator::NavigateToPendingEntry( RenderFrameHostImpl* render_frame_host, NavigationController::ReloadType reload_type) { diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h index 5d7b904..b229ffb 100644 --- a/content/browser/frame_host/navigator.h +++ b/content/browser/frame_host/navigator.h @@ -34,6 +34,9 @@ class RenderFrameHostImpl; // from WebContentsImpl to this interface. class CONTENT_EXPORT Navigator : public base::RefCounted<Navigator> { public: + // Returns the NavigationController associated with this Navigator. + virtual NavigationController* GetController(); + // The RenderFrameHostImpl started a provisional load. virtual void DidStartProvisionalLoad(RenderFrameHostImpl* render_frame_host, int parent_routing_id, diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index 832c58e..64d2421 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc @@ -137,6 +137,10 @@ NavigatorImpl::NavigatorImpl( delegate_(delegate) { } +NavigationController* NavigatorImpl::GetController() { + return controller_; +} + void NavigatorImpl::DidStartProvisionalLoad( RenderFrameHostImpl* render_frame_host, int parent_routing_id, diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h index 47c3f37..1aa9a60 100644 --- a/content/browser/frame_host/navigator_impl.h +++ b/content/browser/frame_host/navigator_impl.h @@ -24,6 +24,7 @@ class CONTENT_EXPORT NavigatorImpl : public Navigator { NavigatorDelegate* delegate); // Navigator implementation. + virtual NavigationController* GetController() OVERRIDE; virtual void DidStartProvisionalLoad(RenderFrameHostImpl* render_frame_host, int parent_routing_id, const GURL& url) OVERRIDE; diff --git a/content/browser/loader/cross_site_resource_handler.cc b/content/browser/loader/cross_site_resource_handler.cc index 8e56397..bf3f0bd 100644 --- a/content/browser/loader/cross_site_resource_handler.cc +++ b/content/browser/loader/cross_site_resource_handler.cc @@ -84,6 +84,20 @@ void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { } } +bool CheckNavigationPolicyOnUI(GURL url, int process_id, int render_frame_id) { + RenderFrameHostImpl* rfh = + RenderFrameHostImpl::FromID(process_id, render_frame_id); + if (!rfh) + return false; + + // TODO(nasko): This check is very simplistic and is used temporarily only + // for --site-per-process. It should be updated to match the check performed + // by RenderFrameHostManager::UpdateRendererStateForNavigate. + return !SiteInstance::IsSameWebSite( + rfh->GetSiteInstance()->GetBrowserContext(), + rfh->GetSiteInstance()->GetSiteURL(), url); +} + } // namespace CrossSiteResourceHandler::CrossSiteResourceHandler( @@ -93,7 +107,8 @@ CrossSiteResourceHandler::CrossSiteResourceHandler( has_started_response_(false), in_cross_site_transition_(false), completed_during_transition_(false), - did_defer_(false) { + did_defer_(false), + weak_ptr_factory_(this) { } CrossSiteResourceHandler::~CrossSiteResourceHandler() { @@ -134,24 +149,14 @@ bool CrossSiteResourceHandler::OnResponseStarted( info->GetContext(), request()->original_url(), request()->url()); // When the --site-per-process flag is passed, we transfer processes for - // cross-site subframe navigations. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) { - GURL referrer(request()->referrer()); - // We skip this for WebUI processes for now, since pages like the NTP host - // cross-site WebUI iframes but don't have referrers. - bool is_webui_process = ChildProcessSecurityPolicyImpl::GetInstance()-> - HasWebUIBindings(info->GetChildID()); - - // TODO(creis): This shouldn't rely on the referrer to determine the parent - // frame's URL. This also doesn't work for hosted apps, due to passing NULL - // to IsSameWebSite. It should be possible to always send the navigation to - // the UI thread to make a policy decision, which could let us eliminate the - // renderer-side check in RenderViewImpl::decidePolicyForNavigation as well. - if (info->GetResourceType() == ResourceType::SUB_FRAME && - !is_webui_process && - !SiteInstance::IsSameWebSite(NULL, request()->url(), referrer)) { - should_transfer = true; - } + // cross-site navigations. This is skipped if a transfer is already required + // or for WebUI processes for now, since pages like the NTP host multiple + // cross-site WebUI iframes. + if (!should_transfer && + CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) && + !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + info->GetChildID())) { + return DeferForNavigationPolicyCheck(info, response, defer); } bool swap_needed = should_transfer || @@ -190,6 +195,15 @@ bool CrossSiteResourceHandler::OnResponseStarted( return true; } +void CrossSiteResourceHandler::ResumeOrTransfer(bool is_transfer) { + if (is_transfer) { + ResourceRequestInfoImpl* info = GetRequestInfo(); + StartCrossSiteTransition(info->GetRequestID(), response_, is_transfer); + } else { + ResumeResponse(); + } +} + bool CrossSiteResourceHandler::OnReadCompleted(int request_id, int bytes_read, bool* defer) { @@ -238,7 +252,6 @@ void CrossSiteResourceHandler::OnResponseCompleted( // WebContentsImpl to swap in the new renderer and destroy the old one. void CrossSiteResourceHandler::ResumeResponse() { DCHECK(request()); - DCHECK(in_cross_site_transition_); in_cross_site_transition_ = false; ResourceRequestInfoImpl* info = GetRequestInfo(); @@ -326,6 +339,38 @@ void CrossSiteResourceHandler::StartCrossSiteTransition( info->should_replace_current_entry()))); } +bool CrossSiteResourceHandler::DeferForNavigationPolicyCheck( + ResourceRequestInfoImpl* info, + ResourceResponse* response, + bool* defer) { + // Store the response_ object internally, since the navigation is deferred + // regardless of whether it will be a transfer or not. + response_ = response; + + // Always defer the navigation to the UI thread to make a policy decision. + // It will send the result back to the IO thread to either resume or + // transfer it to a new renderer. + // TODO(nasko): If the UI thread result is that transfer is required, the + // IO thread will defer to the UI thread again through + // StartCrossSiteTransition. This is unnecessary and the policy check on the + // UI thread should be refactored to avoid the extra hop. + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, + FROM_HERE, + base::Bind(&CheckNavigationPolicyOnUI, + request()->url(), + info->GetChildID(), + info->GetRenderFrameID()), + base::Bind(&CrossSiteResourceHandler::ResumeOrTransfer, + weak_ptr_factory_.GetWeakPtr())); + + // Defer loading until it is known whether the navigation will transfer + // to a new process or continue in the existing one. + *defer = true; + OnDidDefer(); + return true; +} + void CrossSiteResourceHandler::ResumeIfDeferred() { if (did_defer_) { request()->LogUnblocked(); diff --git a/content/browser/loader/cross_site_resource_handler.h b/content/browser/loader/cross_site_resource_handler.h index 9f487a6..df6c7a3 100644 --- a/content/browser/loader/cross_site_resource_handler.h +++ b/content/browser/loader/cross_site_resource_handler.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_LOADER_CROSS_SITE_RESOURCE_HANDLER_H_ #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "content/browser/loader/layered_resource_handler.h" #include "content/common/content_export.h" #include "net/url_request/url_request_status.h" @@ -59,6 +60,13 @@ class CrossSiteResourceHandler : public LayeredResourceHandler { ResourceResponse* response, bool should_transfer); + // Defer the navigation to the UI thread to check whether transfer is required + // or not. Currently only used in --site-per-process. + bool DeferForNavigationPolicyCheck(ResourceRequestInfoImpl* info, + ResourceResponse* response, + bool* defer); + + void ResumeOrTransfer(bool is_transfer); void ResumeIfDeferred(); // Called when about to defer a request. Sets |did_defer_| and logs the @@ -73,6 +81,12 @@ class CrossSiteResourceHandler : public LayeredResourceHandler { std::string completed_security_info_; scoped_refptr<ResourceResponse> response_; + // TODO(nasko): WeakPtr is needed in --site-per-process, since all navigations + // are deferred to the UI thread and come back to IO thread via + // PostTaskAndReplyWithResult. If a transfer is needed, it goes back to the UI + // thread. This can be removed once the code is changed to only do one hop. + base::WeakPtrFactory<CrossSiteResourceHandler> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(CrossSiteResourceHandler); }; diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 9a47b4b..5c3be86 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc @@ -17,6 +17,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" +#include "content/test/content_browser_test_utils_internal.h" #include "net/dns/mock_host_resolver.h" #include "url/gurl.h" @@ -203,13 +204,16 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) { GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); NavigateToURL(shell(), main_url); - StartFrameAtDataURL(); + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = + static_cast<WebContentsImpl*>(shell()->web_contents())-> + GetFrameTree()->root(); SitePerProcessWebContentsObserver observer(shell()->web_contents()); // Load same-site page into iframe. GURL http_url(test_server()->GetURL("files/title1.html")); - EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test")); + NavigateFrameToURL(root->child_at(0), http_url); EXPECT_EQ(http_url, observer.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); @@ -221,22 +225,43 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) { GURL cross_site_url(test_server()->GetURL("files/title2.html")); replace_host.SetHostStr(foo_com); cross_site_url = cross_site_url.ReplaceComponents(replace_host); - EXPECT_TRUE(NavigateIframeToURL(shell(), cross_site_url, "test")); + NavigateFrameToURL(root->child_at(0), cross_site_url); EXPECT_EQ(cross_site_url, observer.navigation_url()); EXPECT_TRUE(observer.navigation_succeeded()); // Ensure that we have created a new process for the subframe. - FrameTreeNode* root = - static_cast<WebContentsImpl*>(shell()->web_contents())-> - GetFrameTree()->root(); ASSERT_EQ(1U, root->child_count()); FrameTreeNode* child = root->child_at(0); + SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance(); + RenderViewHost* rvh = child->current_frame_host()->render_view_host(); + RenderProcessHost* rph = child->current_frame_host()->GetProcess(); + EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh); + EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance); + EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph); + + // Load another cross-site page into the same iframe. + cross_site_url = test_server()->GetURL("files/title3.html"); + std::string bar_com("bar.com"); + replace_host.SetHostStr(bar_com); + cross_site_url = cross_site_url.ReplaceComponents(replace_host); + NavigateFrameToURL(root->child_at(0), cross_site_url); + EXPECT_EQ(cross_site_url, observer.navigation_url()); + EXPECT_TRUE(observer.navigation_succeeded()); + + // Check again that a new process is created and is different from the + // top level one and the previous one. + ASSERT_EQ(1U, root->child_count()); + child = root->child_at(0); EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), child->current_frame_host()->render_view_host()); + EXPECT_NE(rvh, child->current_frame_host()->render_view_host()); EXPECT_NE(shell()->web_contents()->GetSiteInstance(), - child->current_frame_host()->render_view_host()->GetSiteInstance()); + child->current_frame_host()->GetSiteInstance()); + EXPECT_NE(site_instance, + child->current_frame_host()->GetSiteInstance()); EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), child->current_frame_host()->GetProcess()); + EXPECT_NE(rph, child->current_frame_host()->GetProcess()); } // Crash a subframe and ensures its children are cleared from the FrameTree. diff --git a/content/content_tests.gypi b/content/content_tests.gypi index c14b98c..344f690 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -154,6 +154,8 @@ 'gpu/gpu_idirect3d9_mock_win.h', 'test/appcache_test_helper.cc', 'test/appcache_test_helper.h', + 'test/content_browser_test_utils_internal.cc', + 'test/content_browser_test_utils_internal.h', 'test/content_test_suite.cc', 'test/content_test_suite.h', 'test/mock_google_streaming_server.cc', @@ -183,6 +185,8 @@ 'test/test_content_browser_client.h', 'test/test_content_client.cc', 'test/test_content_client.h', + 'test/test_frame_navigation_observer.cc', + 'test/test_frame_navigation_observer.h', 'test/test_render_frame_host.cc', 'test/test_render_frame_host.h', 'test/test_render_frame_host_factory.cc', diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc new file mode 100644 index 0000000..be503b7 --- /dev/null +++ b/content/test/content_browser_test_utils_internal.cc @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/test/content_browser_test_utils_internal.h" + +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/frame_host/navigator.h" +#include "content/test/test_frame_navigation_observer.h" +#include "url/gurl.h" + +namespace content { + +void NavigateFrameToURL(FrameTreeNode* node, const GURL& url) { + TestFrameNavigationObserver observer(node); + NavigationController::LoadURLParams params(url); + params.transition_type = PageTransitionFromInt(PAGE_TRANSITION_LINK); + params.frame_tree_node_id = node->frame_tree_node_id(); + node->navigator()->GetController()->LoadURLWithParams(params); + observer.Wait(); +} + +} // namespace content diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h new file mode 100644 index 0000000..a27aea5 --- /dev/null +++ b/content/test/content_browser_test_utils_internal.h @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_ +#define CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_ + +// A collections of functions designed for use with content_shell based browser +// tests internal to the content/ module. +// Note: If a function here also works with browser_tests, it should be in +// the content public API. + +class GURL; + +namespace content { + +class FrameTreeNode; + +// Navigates the frame represented by |node| to |url|, blocking until the +// navigation finishes. +void NavigateFrameToURL(FrameTreeNode* node, const GURL& url); + +} // namespace content + +#endif // CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_ diff --git a/content/test/test_frame_navigation_observer.cc b/content/test/test_frame_navigation_observer.cc new file mode 100644 index 0000000..83eb093 --- /dev/null +++ b/content/test/test_frame_navigation_observer.cc @@ -0,0 +1,80 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/test/test_frame_navigation_observer.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "content/browser/frame_host/navigation_entry_impl.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/web_contents_observer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +TestFrameNavigationObserver::TestFrameNavigationObserver( + FrameTreeNode* node, + int number_of_navigations) + : WebContentsObserver( + node->current_frame_host()->delegate()->GetAsWebContents()), + frame_tree_node_id_(node->frame_tree_node_id()), + navigation_started_(false), + navigations_completed_(0), + number_of_navigations_(number_of_navigations), + message_loop_runner_(new MessageLoopRunner) { +} + +TestFrameNavigationObserver::TestFrameNavigationObserver( + FrameTreeNode* node) + : WebContentsObserver( + node->current_frame_host()->delegate()->GetAsWebContents()), + frame_tree_node_id_(node->frame_tree_node_id()), + navigation_started_(false), + navigations_completed_(0), + number_of_navigations_(1), + message_loop_runner_(new MessageLoopRunner) { +} + +TestFrameNavigationObserver::~TestFrameNavigationObserver() { +} + +void TestFrameNavigationObserver::Wait() { + message_loop_runner_->Run(); +} + +void TestFrameNavigationObserver::DidStartProvisionalLoadForFrame( + int64 frame_id, + int64 parent_frame_id, + bool is_main_frame, + const GURL& validated_url, + bool is_error_page, + bool is_iframe_srcdoc, + RenderViewHost* render_view_host) { + RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( + render_view_host->GetProcess()->GetID(), frame_id); + if (!rfh) + return; + + if (rfh->frame_tree_node()->frame_tree_node_id() == frame_tree_node_id_) + navigation_started_ = true; +} + +void TestFrameNavigationObserver::DidNavigateAnyFrame( + const LoadCommittedDetails& details, + const FrameNavigateParams& params) { + if (!navigation_started_) + return; + + ++navigations_completed_; + if (navigations_completed_ == number_of_navigations_) { + navigation_started_ = false; + message_loop_runner_->Quit(); + } +} + +} // namespace content diff --git a/content/test/test_frame_navigation_observer.h b/content/test/test_frame_navigation_observer.h new file mode 100644 index 0000000..4cccb34 --- /dev/null +++ b/content/test/test_frame_navigation_observer.h @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_TEST_TEST_FRAME_NAVIGATION_OBSERVER_H_ +#define CONTENT_TEST_TEST_FRAME_NAVIGATION_OBSERVER_H_ + +#include <set> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/test/test_utils.h" + +class GURL; + +namespace content { +class FrameTreeNode; +class RenderFrameHostImpl; +class WebContents; +struct LoadCommittedDetails; + +// For content_browsertests, which run on the UI thread, run a second +// MessageLoop and quit when the navigation in a specific frame completes +// loading. +class TestFrameNavigationObserver : public WebContentsObserver { + public: + // Create and register a new TestFrameNavigationObserver which will track + // navigations performed in the specified |node| of the frame tree. + TestFrameNavigationObserver(FrameTreeNode* node, int number_of_navigations); + // As above but waits for one navigation. + explicit TestFrameNavigationObserver(FrameTreeNode* node); + + virtual ~TestFrameNavigationObserver(); + + // Runs a nested message loop and blocks until the expected number of + // navigations are complete. + void Wait(); + + private: + // WebContentsObserver + virtual void DidStartProvisionalLoadForFrame( + int64 frame_id, + int64 parent_frame_id, + bool is_main_frame, + const GURL& validated_url, + bool is_error_page, + bool is_iframe_srcdoc, + RenderViewHost* render_view_host) OVERRIDE; + virtual void DidNavigateAnyFrame( + const LoadCommittedDetails& details, + const FrameNavigateParams& params) OVERRIDE; + + // The id of the FrameTreeNode in which navigations are peformed. + int frame_tree_node_id_; + + // If true the navigation has started. + bool navigation_started_; + + // The number of navigations that have been completed. + int navigations_completed_; + + // The number of navigations to wait for. + int number_of_navigations_; + + // The MessageLoopRunner used to spin the message loop. + scoped_refptr<MessageLoopRunner> message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(TestFrameNavigationObserver); +}; + +} // namespace content + +#endif // CONTENT_TEST_TEST_FRAME_NAVIGATION_OBSERVER_H_ |