// Copyright (c) 2014 The Chromium Embedded Framework 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 this first to avoid type conflicts with CEF headers. #include "tests/unittests/chromium_includes.h" #include "include/base/cef_bind.h" #include "include/base/cef_scoped_ptr.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/cefclient/client_app.h" #include "tests/unittests/test_handler.h" // Comment out this define to disable the unit test timeout. #define TIMEOUT_ENABLED 1 namespace { // The frame navigation test harness work as follows: // // In the browser process: // 1. TEST() function creates a new FrameNavTestHandler instance with a unique // FrameNavFactoryId. // 2. FrameNavTestHandler calls FrameNavExpectationsFactoryBrowser::FromID to // create a new factory instance. // 3. FrameNavTestHandler calls FrameNavExpectationsFactoryBrowser::Create to // create a new FrameNavExpectationsBrowser instance for the current // navigation. // 4. FrameNavTestHandler retrieves the URL to load via // FrameNavExpectationsBrowser::GetMainURL and calls either CreateBrowser // (for the first navigation) or LoadURL (for the following navigations). // 5. If the renderer process does not already exist CEF creates it with // command-line arguments that specify the FrameNavFactoryId via // FrameNavBrowserTest::OnBeforeChildProcessLaunch. // // In the renderer process: // 6. If the renderer process is newly created FrameNavRendererTest calls // FrameNavExpectationsFactoryRenderer::FromID to create a new factory // instance. // 7. FrameNavRendererTest calls FrameNavExpectationsFactoryRenderer::Create to // create a new FrameNavExpectationsRenderer instance for the current // navigation. // // In both processes: // 8. Callback notifications are sent to the FrameNavExpectations* instances. // // In the renderer process: // 9. When the FrameNavExpectationsRenderer instance determines that the // renderer side of the test is complete it calls SignalComplete which // finalizes and deletes the FrameNavExpectationsRenderer instance and // sends an IPC message to the browser process. // // In the browser process: // 11.FrameNavExpectationsBrowser::OnRendererComplete is called in response to // renderer-side test completion message. // 12.When the FrameNavExpectationsBrowser instance determines that the browser // side of the test is complete it calls SignalComplete which finalizes and // deletes the FrameNavExpectationsBrowser instance. // 13.If FrameNavExpectationsFactoryBrowser::HasMoreNavigations returns false // then DestroyTest is called and the test ends. Otherwise, the navigation // count is incremented and the process repeats starting with step #3. // // // To add a new test case: // 1. Add a new value to the FrameNavFactoryId enumeration. // 2. Provide implementations of FrameNavExpectations*. // 3. Add a case for the new factory ID to FrameNavExpectationsFactory*::FromID. // 4. Implement a TEST() function that creates a FrameNavTestHandler instance // and passes the new factory ID. // // // Run with the `--single-process` command-line flag to see expectation failures // from the renderer process. // // All known factory IDs. enum FrameNavFactoryId { FNF_ID_INVALID = 0, FNF_ID_SINGLE_NAV_HARNESS, FNF_ID_SINGLE_NAV, FNF_ID_MULTI_NAV_HARNESS, FNF_ID_MULTI_NAV, FNF_ID_NESTED_IFRAMES_SAME_ORIGIN, FNF_ID_NESTED_IFRAMES_DIFF_ORIGIN, }; // Command-line argument names. const char kTestArg[] = "test"; const char kTestFactoryIdArg[] = "testfid"; // IPC message name. const char kFrameNavMsg[] = "FrameTest.Navigation"; // Origins used in tests. const char kFrameNavOrigin0[] = "http://tests-framenav0.com/"; const char kFrameNavOrigin1[] = "http://tests-framenav1.com/"; const char kFrameNavOrigin2[] = "http://tests-framenav2.com/"; // Maximum number of navigations. Should be kept synchronized with the number // of kFrameNavOrigin* values. Don't modify this value without checking the // below use cases. const int kMaxMultiNavNavigations = 3; // Global variables identifying the currently running test. bool g_frame_nav_test = false; FrameNavFactoryId g_frame_nav_factory_id = FNF_ID_INVALID; // Abstract base class representing expectations that result from a navigation. class FrameNavExpectations { public: typedef base::Callback)> CompletionCallback; FrameNavExpectations(int nav, bool renderer) : nav_(nav), renderer_(renderer) { } virtual ~FrameNavExpectations() {} // Browser and renderer notifications. virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) { return true; } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) { return true; } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) { return true; } // Final expectations check before this object is deleted. virtual bool Finalize() =0; // Signal that all expectations are completed. Should be called as a result of // notifications. void SignalComplete(CefRefPtr browser) { if (!completion_callback_.is_null()) { // Execute the callback asynchronously to avoid any issues with what's // currently on the stack. CefPostTask((renderer_ ? TID_RENDERER: TID_UI), base::Bind(completion_callback_, browser)); completion_callback_.Reset(); } } // Returns the current navigation count. In the browser process this value // increments over the life span of the FrameNavTestHandler instance. In the // renderer process this value increments over the life span of a single // renderer instance (i.e. cross-origin navigations will cause this value to // reset). int nav() const { return nav_; } // Returns true if this is a renderer-side expectation object. bool renderer() const { return renderer_; } void set_completion_callback(const CompletionCallback& completion_callback) { completion_callback_ = completion_callback; } private: int nav_; bool renderer_; CompletionCallback completion_callback_; }; // Browser process expectations abstact base class. class FrameNavExpectationsBrowser : public FrameNavExpectations { public: explicit FrameNavExpectationsBrowser(int nav) : FrameNavExpectations(nav, false) { } // Loading information. virtual std::string GetMainURL() =0; virtual std::string GetContentForURL(const std::string& url) =0; // Browser-only notifications. virtual bool OnAfterCreated(CefRefPtr browser) { return true; } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame) { return true; } virtual bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) { return true; } // Called when the renderer signals completion. virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) =0; }; // Renderer process expectations abstract base class. class FrameNavExpectationsRenderer : public FrameNavExpectations { public: explicit FrameNavExpectationsRenderer(int nav) : FrameNavExpectations(nav, true) { } // Renderer-only notifications. virtual bool OnBeforeNavigation(CefRefPtr browser, CefRefPtr frame) { return true; } }; // Abstract base class for the factory that creates expectations objects. class FrameNavExpectationsFactory { public: FrameNavExpectationsFactory() {} virtual ~FrameNavExpectationsFactory() {} // Returns the unique ID for this factory type. virtual FrameNavFactoryId GetID() const =0; }; // Browser process expectations factory abstact base class. class FrameNavExpectationsFactoryBrowser : public FrameNavExpectationsFactory { public: FrameNavExpectationsFactoryBrowser() {} // Create a new factory instance of the specified type. static scoped_ptr FromID(FrameNavFactoryId id); // Returns true if there will be more navigations in the browser process // handler. virtual bool HasMoreNavigations() const =0; // Verify final expectations results. virtual bool Finalize() =0; scoped_ptr Create( int nav, const FrameNavExpectations::CompletionCallback& completion_callback) { scoped_ptr expectations; expectations = Create(nav); expectations->set_completion_callback(completion_callback); return expectations.Pass(); } protected: // Implement in the test-specific factory instance. virtual scoped_ptr Create(int nav) =0; }; // Renderer process expectations factory abstact base class. class FrameNavExpectationsFactoryRenderer : public FrameNavExpectationsFactory { public: FrameNavExpectationsFactoryRenderer() {} // Create a new factory instance of the specified type. static scoped_ptr FromID(FrameNavFactoryId id); scoped_ptr Create( int nav, const FrameNavExpectations::CompletionCallback& completion_callback) { scoped_ptr expectations; expectations = Create(nav); expectations->set_completion_callback(completion_callback); return expectations.Pass(); } protected: // Implement in the test-specific factory instance. virtual scoped_ptr Create(int nav) =0; }; // Browser side app delegate. class FrameNavBrowserTest : public ClientApp::BrowserDelegate { public: FrameNavBrowserTest() {} virtual void OnBeforeChildProcessLaunch( CefRefPtr app, CefRefPtr command_line) OVERRIDE { if (!g_frame_nav_test) return; std::stringstream ss; ss << g_frame_nav_factory_id; // Indicate to the render process that the test should be run. command_line->AppendSwitchWithValue(kTestArg, kFrameNavMsg); command_line->AppendSwitchWithValue(kTestFactoryIdArg, ss.str()); } protected: IMPLEMENT_REFCOUNTING(FrameNavBrowserTest); }; // Renderer side handler. class FrameNavRendererTest : public ClientApp::RenderDelegate, public CefLoadHandler { public: FrameNavRendererTest() : run_test_(false), nav_(0) {} virtual void OnRenderThreadCreated( CefRefPtr app, CefRefPtr extra_info) OVERRIDE { // The g_* values will be set when running in single-process mode. if (!g_frame_nav_test) { // Check that the test should be run. CefRefPtr command_line = CefCommandLine::GetGlobalCommandLine(); const std::string& test = command_line->GetSwitchValue(kTestArg); if (test != kFrameNavMsg) return; } FrameNavFactoryId factory_id = g_frame_nav_factory_id; if (factory_id == FNF_ID_INVALID) { // Retrieve the factory ID from the command-line. CefRefPtr command_line = CefCommandLine::GetGlobalCommandLine(); if (command_line->HasSwitch(kTestFactoryIdArg)) { factory_id = static_cast( atoi(command_line->GetSwitchValue( kTestFactoryIdArg).ToString().c_str())); if (factory_id == FNF_ID_INVALID) return; } } run_test_ = true; factory_ = FrameNavExpectationsFactoryRenderer::FromID(factory_id); } virtual CefRefPtr GetLoadHandler( CefRefPtr app) OVERRIDE { if (!run_test_) return NULL; return this; } virtual void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading)) << "isLoading = " << isLoading << ", nav = " << nav_; } virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_; } virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_; } virtual bool OnBeforeNavigation(CefRefPtr app, CefRefPtr browser, CefRefPtr frame, CefRefPtr request, cef_navigation_type_t navigation_type, bool is_redirect) OVERRIDE { if (!run_test_) return false; CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnBeforeNavigation(browser, frame)) << "nav = " << nav_; return false; } protected: // Create a new expectations object if one does not already exist for the // current navigation. void CreateExpectationsIfNecessary() { if (expectations_) return; expectations_ = factory_->Create( nav_, base::Bind(&FrameNavRendererTest::SendTestResults, this)); } // Send the test results. // Will be called via FrameNavExpectations::SignalComplete. void SendTestResults(CefRefPtr browser) { // End of the current expectations object. EXPECT_TRUE(expectations_->Finalize()) << "nav = " << nav_; expectations_.reset(NULL); // Check if the test has failed. bool result = !TestFailed(); // Return the result to the browser process. CefRefPtr return_msg = CefProcessMessage::Create(kFrameNavMsg); CefRefPtr args = return_msg->GetArgumentList(); EXPECT_TRUE(args.get()); EXPECT_TRUE(args->SetInt(0, nav_)); EXPECT_TRUE(args->SetBool(1, result)); EXPECT_TRUE(browser->SendProcessMessage(PID_BROWSER, return_msg)); nav_++; } bool run_test_; int nav_; scoped_ptr factory_; scoped_ptr expectations_; IMPLEMENT_REFCOUNTING(FrameNavRendererTest); }; // Browser side handler. class FrameNavTestHandler : public TestHandler { public: explicit FrameNavTestHandler(FrameNavFactoryId factory_id) : nav_(0), factory_(FrameNavExpectationsFactoryBrowser::FromID(factory_id)) #if defined(TIMEOUT_ENABLED) , weak_ptr_factory_(this) #endif { EXPECT_FALSE(g_frame_nav_test); EXPECT_EQ(FNF_ID_INVALID, g_frame_nav_factory_id); g_frame_nav_test = true; g_frame_nav_factory_id = factory_id; } virtual ~FrameNavTestHandler() { EXPECT_TRUE(got_destroyed_); g_frame_nav_test = false; g_frame_nav_factory_id = FNF_ID_INVALID; } virtual void RunTest() OVERRIDE { // Create the first expectations object. expectations_ = factory_->Create( nav_, base::Bind(&FrameNavTestHandler::RunNextNav, this)); // Create the browser with the initial URL. CreateBrowser(expectations_->GetMainURL()); #if defined(TIMEOUT_ENABLED) // Time out the test after a reasonable period of time. CefPostDelayedTask(TID_UI, base::Bind(&FrameNavTestHandler::DestroyTest, weak_ptr_factory_.GetWeakPtr()), 4000); #endif } // Transition to the next navigation. // Will be called via FrameNavExpectations::SignalComplete. void RunNextNav(CefRefPtr browser) { // End of the current expectations object. EXPECT_TRUE(expectations_->Finalize()); expectations_.reset(NULL); if (!factory_->HasMoreNavigations()) { // End of the test. DestroyTest(); return; } nav_++; // Create the next expectations object. expectations_ = factory_->Create( nav_, base::Bind(&FrameNavTestHandler::RunNextNav, this)); // Load the main URL. browser->GetMainFrame()->LoadURL(expectations_->GetMainURL()); } virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE { TestHandler::OnAfterCreated(browser); EXPECT_TRUE(expectations_->OnAfterCreated(browser)) << "nav = " << nav_; } virtual CefRefPtr GetResourceHandler( CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE { EXPECT_TRUE(expectations_->GetResourceHandler(browser, frame)) << "nav = " << nav_; const std::string& url = request->GetURL(); const std::string& content = expectations_->GetContentForURL(url); EXPECT_TRUE(!content.empty()) << "nav = " << nav_; CefRefPtr stream = CefStreamReader::CreateForData( static_cast(const_cast(content.c_str())), content.length()); return new CefStreamResourceHandler("text/html", stream); } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, bool is_redirect) OVERRIDE { EXPECT_TRUE(expectations_->OnBeforeBrowse(browser, frame)) << "nav = " << nav_; return false; } virtual void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE { EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading)) << "isLoading = " << isLoading << ", nav = " << nav_;; } virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_; } virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_; } virtual bool OnProcessMessageReceived( CefRefPtr browser, CefProcessId source_process, CefRefPtr message) OVERRIDE { if (message->GetName().ToString() == kFrameNavMsg) { // Test that the renderer side succeeded. CefRefPtr args = message->GetArgumentList(); EXPECT_TRUE(args.get()); EXPECT_TRUE(expectations_->OnRendererComplete(browser, args->GetInt(0), args->GetBool(1))) << "nav = " << nav_; return true; } // Message not handled. return false; } virtual void DestroyTest() OVERRIDE { if (got_destroyed_) return; got_destroyed_.yes(); #if defined(TIMEOUT_ENABLED) weak_ptr_factory_.InvalidateWeakPtrs(); #endif // The expectations should have been tested already. EXPECT_FALSE(expectations_.get()); // Test that factory conditions we met. EXPECT_TRUE(factory_->Finalize()) << "nav = " << nav_; TestHandler::DestroyTest(); } int nav_; TrackCallback got_destroyed_; scoped_ptr factory_; scoped_ptr expectations_; #if defined(TIMEOUT_ENABLED) // Must be the last member. base::WeakPtrFactory weak_ptr_factory_; #endif }; // Wait for the the FrameNavTestHandler destructor to be called. This avoids // problems with setting global variables when running multiple tests. void WaitForTestHandlerDestruction() { while (g_frame_nav_test) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); } // Helper for defining frame tests. #define FRAME_TEST(name, factory_id) \ TEST(FrameTest, name) { \ CefRefPtr handler = \ new FrameNavTestHandler(factory_id); \ handler->ExecuteTest(); \ handler = NULL; \ WaitForTestHandlerDestruction(); \ } // Browser process expectations for a single navigation. class FrameNavExpectationsBrowserSingleNav : public FrameNavExpectationsBrowser { public: explicit FrameNavExpectationsBrowserSingleNav(int nav) : FrameNavExpectationsBrowser(nav) { } virtual ~FrameNavExpectationsBrowserSingleNav() { EXPECT_TRUE(got_finalize_); } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (isLoading) { EXPECT_FALSE(got_loading_state_change_start_); got_loading_state_change_start_.yes(); } else { EXPECT_FALSE(got_loading_state_change_end_); got_loading_state_change_end_.yes(); SignalCompleteIfDone(browser); } return true; } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_start_); got_load_start_.yes(); return true; } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); SignalCompleteIfDone(browser); return true; } virtual bool OnAfterCreated(CefRefPtr browser) OVERRIDE { EXPECT_FALSE(got_after_created_); got_after_created_.yes(); return true; } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_before_browse_); got_before_browse_.yes(); return true; } virtual bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_get_resource_handler_); got_get_resource_handler_.yes(); return true; } virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) OVERRIDE { EXPECT_EQ(nav(), renderer_nav); EXPECT_TRUE(renderer_result); EXPECT_FALSE(got_renderer_done_); got_renderer_done_.yes(); SignalCompleteIfDone(browser); return true; } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(got_load_start_); V_EXPECT_TRUE(got_load_end_); V_EXPECT_TRUE(got_loading_state_change_start_); V_EXPECT_TRUE(got_loading_state_change_end_); V_EXPECT_TRUE(got_renderer_done_); V_EXPECT_TRUE(got_after_created_); V_EXPECT_TRUE(got_before_browse_); V_EXPECT_TRUE(got_get_resource_handler_); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser) { if (got_renderer_done_ && got_load_end_ && got_loading_state_change_end_) SignalComplete(browser); } TrackCallback got_load_start_; TrackCallback got_load_end_; TrackCallback got_loading_state_change_start_; TrackCallback got_loading_state_change_end_; TrackCallback got_renderer_done_; TrackCallback got_after_created_; TrackCallback got_before_browse_; TrackCallback got_get_resource_handler_; TrackCallback got_finalize_; }; // Renderer process expectations for a single navigation. class FrameNavExpectationsRendererSingleNav : public FrameNavExpectationsRenderer { public: explicit FrameNavExpectationsRendererSingleNav(int nav) : FrameNavExpectationsRenderer(nav) { } virtual ~FrameNavExpectationsRendererSingleNav() { EXPECT_TRUE(got_finalize_); } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (isLoading) { EXPECT_FALSE(got_loading_state_change_start_); got_loading_state_change_start_.yes(); } else { EXPECT_FALSE(got_loading_state_change_end_); got_loading_state_change_end_.yes(); SignalCompleteIfDone(browser); } return true; } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_start_); got_load_start_.yes(); return true; } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); SignalCompleteIfDone(browser); return true; } virtual bool OnBeforeNavigation(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_before_navigation_); got_before_navigation_.yes(); return true; } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(got_load_start_); V_EXPECT_TRUE(got_load_end_); V_EXPECT_TRUE(got_loading_state_change_start_); V_EXPECT_TRUE(got_loading_state_change_end_); V_EXPECT_TRUE(got_before_navigation_); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser) { if (got_load_end_ && got_loading_state_change_end_) SignalComplete(browser); } TrackCallback got_load_start_; TrackCallback got_load_end_; TrackCallback got_loading_state_change_start_; TrackCallback got_loading_state_change_end_; TrackCallback got_before_navigation_; TrackCallback got_finalize_; }; // Test that the single nav harness works. class FrameNavExpectationsBrowserTestSingleNavHarness : public FrameNavExpectationsBrowserSingleNav { public: typedef FrameNavExpectationsBrowserSingleNav parent; explicit FrameNavExpectationsBrowserTestSingleNavHarness(int nav) : parent(nav) { } virtual ~FrameNavExpectationsBrowserTestSingleNavHarness() { EXPECT_TRUE(got_finalize_); } virtual std::string GetMainURL() OVERRIDE { EXPECT_FALSE(got_get_main_url_); got_get_main_url_.yes(); return kFrameNavOrigin0; } virtual std::string GetContentForURL(const std::string& url) OVERRIDE { EXPECT_FALSE(got_get_content_for_url_); got_get_content_for_url_.yes(); EXPECT_STREQ(kFrameNavOrigin0, url.c_str()); return "Nav"; } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_DECLARE(); V_EXPECT_TRUE(got_get_main_url_); V_EXPECT_TRUE(got_get_content_for_url_); V_EXPECT_TRUE(parent::Finalize()); V_RETURN(); } private: TrackCallback got_get_main_url_; TrackCallback got_get_content_for_url_; TrackCallback got_finalize_; }; class FrameNavExpectationsRendererTestSingleNavHarness : public FrameNavExpectationsRendererSingleNav { public: typedef FrameNavExpectationsRendererSingleNav parent; explicit FrameNavExpectationsRendererTestSingleNavHarness(int nav) : parent(nav) { } virtual ~FrameNavExpectationsRendererTestSingleNavHarness() { EXPECT_TRUE(got_finalize_); } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); return parent::Finalize(); } private: TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryBrowserTestSingleNavHarness : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestSingleNavHarness() {} virtual ~FrameNavExpectationsFactoryBrowserTestSingleNavHarness() { EXPECT_TRUE(got_finalize_); } virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_SINGLE_NAV_HARNESS; } virtual bool HasMoreNavigations() const OVERRIDE { EXPECT_FALSE(got_get_browser_navigation_count_); got_get_browser_navigation_count_.yes(); return false; } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_DECLARE(); V_EXPECT_TRUE(got_get_browser_navigation_count_); V_EXPECT_TRUE(got_create_); V_RETURN(); } protected: virtual scoped_ptr Create(int nav) OVERRIDE { EXPECT_FALSE(got_create_); got_create_.yes(); return make_scoped_ptr( new FrameNavExpectationsBrowserTestSingleNavHarness(nav)); } private: mutable TrackCallback got_get_browser_navigation_count_; TrackCallback got_create_; TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryRendererTestSingleNavHarness : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestSingleNavHarness() {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_SINGLE_NAV_HARNESS; } protected: virtual scoped_ptr Create(int nav) OVERRIDE { return make_scoped_ptr( new FrameNavExpectationsRendererTestSingleNavHarness(nav)); } }; } // namespace // Test that the single nav harness works. FRAME_TEST(SingleNavHarness, FNF_ID_SINGLE_NAV_HARNESS) namespace { bool VerifySingleBrowserFrame(CefRefPtr browser, CefRefPtr frame, bool frame_should_exist, const std::string& expected_url) { V_DECLARE(); V_EXPECT_TRUE(frame.get()); V_EXPECT_TRUE(frame->IsValid()); if (frame_should_exist) { V_EXPECT_TRUE(frame->GetIdentifier() >= 0); } else { V_EXPECT_TRUE(frame->GetIdentifier() == -4); // kInvalidFrameId } V_EXPECT_TRUE(frame->IsValid()); V_EXPECT_TRUE(frame->IsMain()); V_EXPECT_TRUE(frame->IsFocused()); V_EXPECT_FALSE(frame->GetParent().get()); V_EXPECT_TRUE(frame->GetName().empty()); V_EXPECT_TRUE(browser->GetIdentifier() == frame->GetBrowser()->GetIdentifier()); const std::string& frame_url = frame->GetURL(); V_EXPECT_TRUE(frame_url == expected_url) << "frame_url = " << frame_url << ", expected_url = " << expected_url; V_RETURN(); } bool VerifySingleBrowserFrames(CefRefPtr browser, CefRefPtr frame, bool frame_should_exist, const std::string& expected_url) { V_DECLARE(); V_EXPECT_TRUE(browser.get()); // |frame| may be NULL for callbacks that don't specify one. if (frame.get()) { V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, frame, frame_should_exist, expected_url)); } CefRefPtr main_frame = browser->GetMainFrame(); V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, main_frame, frame_should_exist, expected_url)); CefRefPtr focused_frame = browser->GetFocusedFrame(); V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, focused_frame, frame_should_exist, expected_url)); size_t frame_count = browser->GetFrameCount(); if (frame_should_exist) { V_EXPECT_TRUE(frame_count == 1U); std::vector identifiers; browser->GetFrameIdentifiers(identifiers); V_EXPECT_TRUE(identifiers.size() == 1U); if (identifiers.size() == 1U) { V_EXPECT_TRUE(identifiers[0] == main_frame->GetIdentifier()); V_EXPECT_TRUE(identifiers[0] == focused_frame->GetIdentifier()); } std::vector names; browser->GetFrameNames(names); V_EXPECT_TRUE(names.size() == 1U); if (names.size() == 1U) { V_EXPECT_TRUE(names[0].ToString() == main_frame->GetName().ToString()); V_EXPECT_TRUE(names[0].ToString() == focused_frame->GetName().ToString()); } } else { V_EXPECT_TRUE(frame_count == 0U); } V_RETURN(); } // Test that single navigation works. class FrameNavExpectationsBrowserTestSingleNav : public FrameNavExpectationsBrowserSingleNav { public: typedef FrameNavExpectationsBrowserSingleNav parent; explicit FrameNavExpectationsBrowserTestSingleNav(int nav) : parent(nav) { } virtual std::string GetMainURL() OVERRIDE { return kFrameNavOrigin0; } virtual std::string GetContentForURL(const std::string& url) OVERRIDE { return "Nav"; } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { V_DECLARE(); if (isLoading) { // No frame exists before the first load. V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, false, std::string())); } else { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, kFrameNavOrigin0)); } V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } virtual bool OnAfterCreated(CefRefPtr browser) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, false, std::string())); V_EXPECT_TRUE(parent::OnAfterCreated(browser)); V_RETURN(); } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, std::string())); V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame)); V_RETURN(); } virtual bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, std::string())); V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame)); V_RETURN(); } virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) OVERRIDE { return parent::OnRendererComplete(browser, renderer_nav, renderer_result); } virtual bool Finalize() OVERRIDE { return parent::Finalize(); } }; class FrameNavExpectationsRendererTestSingleNav : public FrameNavExpectationsRendererSingleNav { public: typedef FrameNavExpectationsRendererSingleNav parent; explicit FrameNavExpectationsRendererTestSingleNav(int nav) : parent(nav) { } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { V_DECLARE(); // A frame should always exist in the renderer process. if (isLoading) { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, std::string())); } else { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, kFrameNavOrigin0)); } V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } virtual bool Finalize() OVERRIDE { return parent::Finalize(); } }; class FrameNavExpectationsFactoryBrowserTestSingleNav : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestSingleNav() {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_SINGLE_NAV; } virtual bool HasMoreNavigations() const OVERRIDE { return false; } virtual bool Finalize() OVERRIDE { return true; } protected: virtual scoped_ptr Create(int nav) OVERRIDE { return make_scoped_ptr( new FrameNavExpectationsBrowserTestSingleNav(nav)); } }; class FrameNavExpectationsFactoryRendererTestSingleNav : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestSingleNav() {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_SINGLE_NAV; } protected: virtual scoped_ptr Create(int nav) OVERRIDE { return make_scoped_ptr( new FrameNavExpectationsRendererTestSingleNav(nav)); } }; } // namespace // Test that single navigation works. FRAME_TEST(SingleNav, FNF_ID_SINGLE_NAV) namespace { // Browser process expectations for a multiple navigations. class FrameNavExpectationsBrowserMultiNav : public FrameNavExpectationsBrowser { public: explicit FrameNavExpectationsBrowserMultiNav(int nav) : FrameNavExpectationsBrowser(nav) { } virtual ~FrameNavExpectationsBrowserMultiNav() { EXPECT_TRUE(got_finalize_); } // Returns true if all navigation is done. virtual bool IsNavigationDone() const =0; virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) SignalCompleteIfDone(browser); return true; } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { SignalCompleteIfDone(browser); return true; } virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) OVERRIDE { EXPECT_TRUE(renderer_result); SignalCompleteIfDone(browser); return true; } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser) { if (IsNavigationDone()) SignalComplete(browser); } TrackCallback got_finalize_; }; // Renderer process expectations for a multiple navigations. class FrameNavExpectationsRendererMultiNav : public FrameNavExpectationsRenderer { public: explicit FrameNavExpectationsRendererMultiNav(int nav) : FrameNavExpectationsRenderer(nav) { } virtual ~FrameNavExpectationsRendererMultiNav() { EXPECT_TRUE(got_finalize_); } // Returns true if all navigation is done. virtual bool IsNavigationDone() const =0; virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) SignalCompleteIfDone(browser); return true; } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { SignalCompleteIfDone(browser); return true; } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser) { if (IsNavigationDone()) SignalComplete(browser); } TrackCallback got_finalize_; }; // Create a URL containing the nav number. std::string GetMultiNavURL(const std::string& origin, int nav) { std::stringstream ss; ss << origin << "nav" << nav << ".html"; return ss.str(); } // Extract the nav number from the URL. int GetNavFromMultiNavURL(const std::string& url) { const size_t start = url.find("/nav"); const size_t end = url.find(".html", start); EXPECT_TRUE(start < end && start > 0U); const std::string& nav = url.substr(start + 4, end - start - 4); return atoi(nav.c_str()); } // Extract the origin from the URL. std::string GetOriginFromMultiNavURL(const std::string& url) { const size_t pos = url.rfind("/"); EXPECT_TRUE(pos > 0U); return url.substr(0, pos + 1); } // Test that the multi nav harness works. class FrameNavExpectationsBrowserTestMultiNavHarness : public FrameNavExpectationsBrowserMultiNav { public: typedef FrameNavExpectationsBrowserMultiNav parent; explicit FrameNavExpectationsBrowserTestMultiNavHarness(int nav) : parent(nav), navigation_done_count_(0) { } virtual ~FrameNavExpectationsBrowserTestMultiNavHarness() { EXPECT_TRUE(got_finalize_); } virtual std::string GetMainURL() OVERRIDE { EXPECT_FALSE(got_get_main_url_); got_get_main_url_.yes(); return GetMultiNavURL(kFrameNavOrigin0, nav()); } virtual std::string GetContentForURL(const std::string& url) OVERRIDE { EXPECT_FALSE(got_get_content_for_url_); got_get_content_for_url_.yes(); EXPECT_STREQ(GetMultiNavURL(kFrameNavOrigin0, nav()).c_str(), url.c_str()); return "Nav"; } virtual bool IsNavigationDone() const OVERRIDE { navigation_done_count_++; return got_load_state_change_done_ && got_load_end_ && got_renderer_complete_; } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) { EXPECT_FALSE(got_load_state_change_done_); got_load_state_change_done_.yes(); } return parent::OnLoadingStateChange(browser, isLoading); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); return parent::OnLoadEnd(browser, frame); } virtual bool OnAfterCreated(CefRefPtr browser) OVERRIDE { EXPECT_FALSE(got_on_after_created_); got_on_after_created_.yes(); return parent::OnAfterCreated(browser); } virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) OVERRIDE { EXPECT_FALSE(got_renderer_complete_); got_renderer_complete_.yes(); EXPECT_EQ(nav(), renderer_nav); return parent::OnRendererComplete(browser, renderer_nav, renderer_result); } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_DECLARE(); V_EXPECT_TRUE(got_get_main_url_); V_EXPECT_TRUE(got_get_content_for_url_); V_EXPECT_TRUE(got_load_state_change_done_); V_EXPECT_TRUE(got_load_end_); if (nav() == 0) { V_EXPECT_TRUE(got_on_after_created_); } else { V_EXPECT_FALSE(got_on_after_created_); } V_EXPECT_TRUE(got_renderer_complete_); V_EXPECT_TRUE(navigation_done_count_ == 3); V_EXPECT_TRUE(parent::Finalize()); V_RETURN(); } private: TrackCallback got_get_main_url_; TrackCallback got_get_content_for_url_; TrackCallback got_load_state_change_done_; TrackCallback got_load_end_; TrackCallback got_on_after_created_; TrackCallback got_renderer_complete_; mutable int navigation_done_count_; TrackCallback got_finalize_; }; class FrameNavExpectationsRendererTestMultiNavHarness : public FrameNavExpectationsRendererMultiNav { public: typedef FrameNavExpectationsRendererMultiNav parent; explicit FrameNavExpectationsRendererTestMultiNavHarness(int nav) : parent(nav), navigation_done_count_(0) { } virtual ~FrameNavExpectationsRendererTestMultiNavHarness() { EXPECT_TRUE(got_finalize_); } virtual bool IsNavigationDone() const OVERRIDE { navigation_done_count_++; return got_load_state_change_done_ && got_load_end_; } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) { EXPECT_FALSE(got_load_state_change_done_); got_load_state_change_done_.yes(); } return parent::OnLoadingStateChange(browser, isLoading); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); return parent::OnLoadEnd(browser, frame); } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_DECLARE(); V_EXPECT_TRUE(got_load_state_change_done_); V_EXPECT_TRUE(got_load_end_); V_EXPECT_TRUE(navigation_done_count_ == 2); V_EXPECT_TRUE(parent::Finalize()); V_RETURN(); } private: TrackCallback got_load_state_change_done_; TrackCallback got_load_end_; mutable int navigation_done_count_; TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryBrowserTestMultiNavHarness : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestMultiNavHarness() : get_browser_navigation_count_(0), create_count_(0) {} virtual ~FrameNavExpectationsFactoryBrowserTestMultiNavHarness() { EXPECT_TRUE(got_finalize_); } virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_MULTI_NAV_HARNESS; } virtual bool HasMoreNavigations() const OVERRIDE { get_browser_navigation_count_++; return (get_browser_navigation_count_ < kMaxMultiNavNavigations); } virtual bool Finalize() OVERRIDE { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_DECLARE(); V_EXPECT_TRUE(get_browser_navigation_count_ == kMaxMultiNavNavigations); V_EXPECT_TRUE(create_count_ == kMaxMultiNavNavigations); V_RETURN(); } protected: virtual scoped_ptr Create(int nav) OVERRIDE { create_count_++; return make_scoped_ptr( new FrameNavExpectationsBrowserTestMultiNavHarness(nav)); } private: mutable int get_browser_navigation_count_; int create_count_; TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryRendererTestMultiNavHarness : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestMultiNavHarness() {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_MULTI_NAV_HARNESS; } protected: virtual scoped_ptr Create(int nav) OVERRIDE { return make_scoped_ptr( new FrameNavExpectationsRendererTestMultiNavHarness(nav)); } }; } // namespace // Test that the multiple nav harness works. FRAME_TEST(MultiNavHarness, FNF_ID_MULTI_NAV_HARNESS) namespace { // Test that multiple navigation works. class FrameNavExpectationsBrowserTestMultiNav : public FrameNavExpectationsBrowserMultiNav { public: typedef FrameNavExpectationsBrowserMultiNav parent; explicit FrameNavExpectationsBrowserTestMultiNav(int nav) : parent(nav) { } virtual std::string GetMainURL() OVERRIDE { return GetMultiNavURL(kFrameNavOrigin0, nav()); } virtual std::string GetContentForURL(const std::string& url) OVERRIDE { return "Nav"; } virtual bool IsNavigationDone() const OVERRIDE { return got_load_state_change_done_ && got_load_end_ && got_renderer_complete_; } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) got_load_state_change_done_.yes(); V_DECLARE(); // A frame should exist in all cases except for the very first load. if (isLoading && nav() == 0) { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, false, std::string())); } else if (isLoading) { // Expect the URL from the previous load. V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, GetPreviousMainURL())); } else { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, GetMainURL())); } V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { got_load_end_.yes(); V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } virtual bool OnAfterCreated(CefRefPtr browser) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, false, std::string())); V_EXPECT_TRUE(parent::OnAfterCreated(browser)); V_RETURN(); } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); std::string expected_url; if (nav() > 0) expected_url = GetPreviousMainURL(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, expected_url)); V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame)); V_RETURN(); } virtual bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); std::string expected_url; if (nav() > 0) expected_url = GetPreviousMainURL(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, expected_url)); V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame)); V_RETURN(); } virtual bool OnRendererComplete(CefRefPtr browser, int renderer_nav, bool renderer_result) OVERRIDE { got_renderer_complete_.yes(); V_DECLARE(); V_EXPECT_TRUE(nav() == renderer_nav); V_EXPECT_TRUE(parent::OnRendererComplete(browser, renderer_nav, renderer_result)); V_RETURN(); } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(got_load_state_change_done_); V_EXPECT_TRUE(got_load_end_); V_EXPECT_TRUE(got_renderer_complete_); V_EXPECT_TRUE(parent::Finalize()); V_RETURN(); } private: // Helper for VerifySingleBrowserFrames. std::string GetPreviousMainURL() { EXPECT_GT(nav(), 0); return GetMultiNavURL(kFrameNavOrigin0, nav() - 1); } TrackCallback got_load_state_change_done_; TrackCallback got_load_end_; TrackCallback got_renderer_complete_; }; class FrameNavExpectationsRendererTestMultiNav : public FrameNavExpectationsRendererMultiNav { public: typedef FrameNavExpectationsRendererMultiNav parent; explicit FrameNavExpectationsRendererTestMultiNav(int nav) : parent(nav) { } virtual bool IsNavigationDone() const OVERRIDE { return got_load_state_change_done_ && got_load_end_; } virtual bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) OVERRIDE { if (!isLoading) got_load_state_change_done_.yes(); V_DECLARE(); // A frame should always exist in the renderer process. if (isLoading) { std::string expected_url; if (nav() > 0) expected_url = GetPreviousMainURL(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, expected_url)); } else { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, true, GetMainURL())); } V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } virtual bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } virtual bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) OVERRIDE { got_load_end_.yes(); V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, true, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(got_load_state_change_done_); V_EXPECT_TRUE(got_load_end_); V_EXPECT_TRUE(parent::Finalize()); V_RETURN(); } private: // Helpers for calling VerifySingleBrowserFrames. std::string GetMainURL() const { return GetMultiNavURL(kFrameNavOrigin0, nav()); } std::string GetPreviousMainURL() { EXPECT_GT(nav(), 0); return GetMultiNavURL(kFrameNavOrigin0, nav() - 1); } TrackCallback got_load_state_change_done_; TrackCallback got_load_end_; }; class FrameNavExpectationsFactoryBrowserTestMultiNav : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestMultiNav() : nav_count_(0) {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_MULTI_NAV; } virtual bool HasMoreNavigations() const OVERRIDE { return (nav_count_ < kMaxMultiNavNavigations); } virtual bool Finalize() OVERRIDE { V_DECLARE(); V_EXPECT_TRUE(nav_count_ == kMaxMultiNavNavigations); V_RETURN(); } protected: virtual scoped_ptr Create(int nav) OVERRIDE { nav_count_++; return make_scoped_ptr( new FrameNavExpectationsBrowserTestMultiNav(nav)); } private: int nav_count_; }; class FrameNavExpectationsFactoryRendererTestMultiNav : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestMultiNav() {} virtual FrameNavFactoryId GetID() const OVERRIDE { return FNF_ID_MULTI_NAV; } protected: virtual scoped_ptr Create(int nav) OVERRIDE { return make_scoped_ptr( new FrameNavExpectationsRendererTestMultiNav(nav)); } }; } // namespace // Test that multiple navigation works. FRAME_TEST(MultiNav, FNF_ID_MULTI_NAV) namespace { const char kFrame0Name[] = ""; const char kFrame1Name[] = "nav2"; const char kFrame2Name[] = "-->"; bool VerifyBrowserIframe(CefRefPtr browser, CefRefPtr frame, const std::string& origin, int frame_number) { V_DECLARE(); // frame0 contains frame1 contains frame2. CefRefPtr frame0, frame1, frame2; CefRefPtr frame0b, frame1b, frame2b; int64 frame0id, frame1id, frame2id; std::string frame0url, frame1url, frame2url; // Find frames by name. frame0 = browser->GetFrame(kFrame0Name); V_EXPECT_TRUE(frame0.get()); frame1 = browser->GetFrame(kFrame1Name); V_EXPECT_TRUE(frame1.get()); frame2 = browser->GetFrame(kFrame2Name); V_EXPECT_TRUE(frame2.get()); // Verify that the name matches. V_EXPECT_TRUE(frame0->GetName().ToString() == kFrame0Name); V_EXPECT_TRUE(frame1->GetName().ToString() == kFrame1Name); V_EXPECT_TRUE(frame2->GetName().ToString() == kFrame2Name); // Verify that the URL matches. frame0url = GetMultiNavURL(origin, 0); V_EXPECT_TRUE(frame0->GetURL() == frame0url); frame1url = GetMultiNavURL(origin, 1); V_EXPECT_TRUE(frame1->GetURL() == frame1url); frame2url = GetMultiNavURL(origin, 2); V_EXPECT_TRUE(frame2->GetURL() == frame2url); // Verify that the frame id is valid. frame0id = frame0->GetIdentifier(); V_EXPECT_TRUE(frame0id > 0); frame1id = frame1->GetIdentifier(); V_EXPECT_TRUE(frame1id > 0); frame2id = frame2->GetIdentifier(); V_EXPECT_TRUE(frame2id > 0); // Verify that the current frame has the correct id. if (frame_number == 0) { V_EXPECT_TRUE(frame->GetIdentifier() == frame0id); } else if (frame_number == 1) { V_EXPECT_TRUE(frame->GetIdentifier() == frame1id); } else if (frame_number == 2) { V_EXPECT_TRUE(frame->GetIdentifier() == frame2id); } // Find frames by id. frame0b = browser->GetFrame(frame0->GetIdentifier()); V_EXPECT_TRUE(frame0b.get()); frame1b = browser->GetFrame(frame1->GetIdentifier()); V_EXPECT_TRUE(frame1b.get()); frame2b = browser->GetFrame(frame2->GetIdentifier()); V_EXPECT_TRUE(frame2b.get()); // Verify that the id matches. V_EXPECT_TRUE(frame0b->GetIdentifier() == frame0id); V_EXPECT_TRUE(frame1b->GetIdentifier() == frame1id); V_EXPECT_TRUE(frame2b->GetIdentifier() == frame2id); V_EXPECT_TRUE(browser->GetFrameCount() == 3U); // Verify the GetFrameNames result. std::vector names; browser->GetFrameNames(names); V_EXPECT_TRUE(names.size() == 3U); V_EXPECT_TRUE(names[0].ToString() == kFrame0Name); V_EXPECT_TRUE(names[1].ToString() == kFrame1Name); V_EXPECT_TRUE(names[2].ToString() == kFrame2Name); // Verify the GetFrameIdentifiers result. std::vector idents; browser->GetFrameIdentifiers(idents); V_EXPECT_TRUE(idents.size() == 3U); V_EXPECT_TRUE(idents[0] == frame0->GetIdentifier()); V_EXPECT_TRUE(idents[1] == frame1->GetIdentifier()); V_EXPECT_TRUE(idents[2] == frame2->GetIdentifier()); // Verify parent hierarchy. V_EXPECT_FALSE(frame0->GetParent().get()); V_EXPECT_TRUE(frame1->GetParent()->GetIdentifier() == frame0id); V_EXPECT_TRUE(frame2->GetParent()->GetIdentifier() == frame1id); V_RETURN(); } // Test that nested iframes work. class FrameNavExpectationsBrowserTestNestedIframes : public FrameNavExpectationsBrowserMultiNav { public: typedef FrameNavExpectationsBrowserMultiNav parent; FrameNavExpectationsBrowserTestNestedIframes(int nav, bool same_origin) : parent(nav), same_origin_(same_origin) { // In the browser process we can rely on the |nav| value to determine the // origin. if (same_origin) { origin_ = kFrameNavOrigin0; } else switch (nav) { case 0: origin_ = kFrameNavOrigin0; break; case 1: origin_ = kFrameNavOrigin1; break; case 2: origin_ = kFrameNavOrigin2; break; default: EXPECT_TRUE(false); // Not reached. break; } } virtual std::string GetMainURL() OVERRIDE { // Load the first (main) frame. return GetMultiNavURL(origin_, 0); } virtual std::string GetContentForURL(const std::string& url) OVERRIDE { const int frame_number = GetNavFromMultiNavURL(url); switch (frame_number) { case 0: // Frame 0. Contains a named iframe. return "Nav1