// 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 "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 "tests/ceftests/test_handler.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" #include "tests/shared/browser/client_app_browser.h" #include "tests/shared/renderer/client_app_renderer.h" using client::ClientAppBrowser; using client::ClientAppRenderer; 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, }; // IPC message name. const char kFrameNavMsg[] = "FrameTest.Navigation"; // Extra info parameter keys. const char kFrameNavTestCmdKey[] = "frame-nav-test"; // 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/"; const char kFrameNavOrigin3[] = "http://tests-framenav3.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 = 4; // Abstract base class representing expectations that result from a navigation. class FrameNavExpectations { public: typedef base::Callback, CefRefPtr)> 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, CefRefPtr frame) { 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, frame)); 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 abstract 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) { EXPECT_TRUE(browser.get()); return true; } virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, const std::string& url) { EXPECT_TRUE(browser.get()); EXPECT_TRUE(frame.get()); EXPECT_FALSE(url.empty()); return true; } virtual bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) { EXPECT_TRUE(browser.get()); EXPECT_TRUE(frame.get()); return true; } // Called when the renderer signals completion. virtual bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, 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) {} }; // 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; }; // Renderer side handler. class FrameNavRendererTest : public ClientAppRenderer::Delegate, public CefLoadHandler { public: FrameNavRendererTest() : run_test_(false), nav_(0) {} void OnBrowserCreated(CefRefPtr app, CefRefPtr browser, CefRefPtr extra_info) override { if (!extra_info->HasKey(kFrameNavTestCmdKey)) return; FrameNavFactoryId factory_id = static_cast(extra_info->GetInt(kFrameNavTestCmdKey)); run_test_ = factory_id != FNF_ID_INVALID; if (!run_test_) return; factory_ = FrameNavExpectationsFactoryRenderer::FromID(factory_id); } CefRefPtr GetLoadHandler( CefRefPtr app) override { if (!run_test_) return NULL; return this; } void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) override { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading)) << "isLoading = " << isLoading << ", nav = " << nav_; } void OnLoadStart(CefRefPtr browser, CefRefPtr frame, TransitionType transition_type) override { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_; } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { CreateExpectationsIfNecessary(); EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_; } 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, CefRefPtr frame) { // 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)); const int64 frame_id = frame->GetIdentifier(); EXPECT_TRUE(args->SetInt(2, CefInt64GetLow(frame_id))); EXPECT_TRUE(args->SetInt(3, CefInt64GetHigh(frame_id))); frame->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)) {} ~FrameNavTestHandler() override { EXPECT_TRUE(got_destroyed_); } void RunTest() override { // Create the first expectations object. expectations_ = factory_->Create( nav_, base::Bind(&FrameNavTestHandler::RunNextNav, this)); CefRefPtr extra_info = CefDictionaryValue::Create(); extra_info->SetInt(kFrameNavTestCmdKey, factory_->GetID()); // Create the browser with the initial URL. CreateBrowser(expectations_->GetMainURL(), NULL, extra_info); // Time out the test after a reasonable period of time. SetTestTimeout(15000); } // Transition to the next navigation. // Will be called via FrameNavExpectations::SignalComplete. void RunNextNav(CefRefPtr browser, CefRefPtr frame) { // 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()); } void OnAfterCreated(CefRefPtr browser) override { TestHandler::OnAfterCreated(browser); EXPECT_TRUE(expectations_->OnAfterCreated(browser)) << "nav = " << nav_; } 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); } bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, bool user_gesture, bool is_redirect) override { EXPECT_TRUE( expectations_->OnBeforeBrowse(browser, frame, request->GetURL())) << "nav = " << nav_; return false; } void OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) override { EXPECT_TRUE(expectations_->OnLoadingStateChange(browser, isLoading)) << "isLoading = " << isLoading << ", nav = " << nav_; ; } void OnLoadStart(CefRefPtr browser, CefRefPtr frame, TransitionType transition_type) override { EXPECT_TRUE(expectations_->OnLoadStart(browser, frame)) << "nav = " << nav_; } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { EXPECT_TRUE(expectations_->OnLoadEnd(browser, frame)) << "nav = " << nav_; } bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, 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, frame, args->GetInt(0), args->GetBool(1))) << "nav = " << nav_; // Test that browser and render process frame IDs match. const int64 frame_id = CefInt64Set(args->GetInt(2), args->GetInt(3)); EXPECT_EQ(frame->GetIdentifier(), frame_id); return true; } // Message not handled. return false; } void DestroyTest() override { if (got_destroyed_) return; got_destroyed_.yes(); // 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_; IMPLEMENT_REFCOUNTING(FrameNavTestHandler); }; // Helper for defining frame tests. #define FRAME_TEST(name, factory_id) \ TEST(FrameTest, name) { \ CefRefPtr handler = \ new FrameNavTestHandler(factory_id); \ handler->ExecuteTest(); \ ReleaseAndWaitForDestructor(handler); \ } // Browser process expectations for a single navigation. class FrameNavExpectationsBrowserSingleNav : public FrameNavExpectationsBrowser { public: explicit FrameNavExpectationsBrowserSingleNav(int nav) : FrameNavExpectationsBrowser(nav) {} ~FrameNavExpectationsBrowserSingleNav() override { EXPECT_TRUE(got_finalize_); } 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, browser->GetMainFrame()); } return true; } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_start_); got_load_start_.yes(); return true; } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); SignalCompleteIfDone(browser, frame); return true; } bool OnAfterCreated(CefRefPtr browser) override { EXPECT_FALSE(got_after_created_); got_after_created_.yes(); return true; } bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, const std::string& url) override { EXPECT_FALSE(got_before_browse_); got_before_browse_.yes(); return true; } bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_get_resource_handler_); got_get_resource_handler_.yes(); return true; } bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, 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, frame); return true; } 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, CefRefPtr frame) { if (got_renderer_done_ && got_load_end_ && got_loading_state_change_end_) SignalComplete(browser, frame); } 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) {} ~FrameNavExpectationsRendererSingleNav() override { EXPECT_TRUE(got_finalize_); } 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, browser->GetMainFrame()); } return true; } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_start_); got_load_start_.yes(); return true; } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); SignalCompleteIfDone(browser, frame); return true; } 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_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser, CefRefPtr frame) { if (got_load_end_ && got_loading_state_change_end_) SignalComplete(browser, frame); } TrackCallback got_load_start_; TrackCallback got_load_end_; TrackCallback got_loading_state_change_start_; TrackCallback got_loading_state_change_end_; TrackCallback got_finalize_; }; // Test that the single nav harness works. class FrameNavExpectationsBrowserTestSingleNavHarness : public FrameNavExpectationsBrowserSingleNav { public: typedef FrameNavExpectationsBrowserSingleNav parent; explicit FrameNavExpectationsBrowserTestSingleNavHarness(int nav) : parent(nav) {} ~FrameNavExpectationsBrowserTestSingleNavHarness() override { EXPECT_TRUE(got_finalize_); } std::string GetMainURL() override { EXPECT_FALSE(got_get_main_url_); got_get_main_url_.yes(); return kFrameNavOrigin0; } 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"; } 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) {} ~FrameNavExpectationsRendererTestSingleNavHarness() override { EXPECT_TRUE(got_finalize_); } bool Finalize() override { EXPECT_FALSE(got_finalize_); got_finalize_.yes(); return parent::Finalize(); } private: TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryBrowserTestSingleNavHarness : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestSingleNavHarness() {} ~FrameNavExpectationsFactoryBrowserTestSingleNavHarness() override { EXPECT_TRUE(got_finalize_); } FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV_HARNESS; } bool HasMoreNavigations() const override { EXPECT_FALSE(got_get_browser_navigation_count_); got_get_browser_navigation_count_.yes(); return false; } 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: scoped_ptr Create(int nav) override { EXPECT_FALSE(got_create_); got_create_.yes(); return 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() {} FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV_HARNESS; } protected: scoped_ptr Create(int nav) override { return 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, const std::string& expected_url) { V_DECLARE(); V_EXPECT_TRUE(frame.get()); V_EXPECT_TRUE(frame->IsValid()); const int64 frame_id = frame->GetIdentifier(); V_EXPECT_TRUE(frame_id > 0) << frame_id; 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, 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, expected_url)); } CefRefPtr main_frame = browser->GetMainFrame(); V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, main_frame, expected_url)); CefRefPtr focused_frame = browser->GetFocusedFrame(); V_EXPECT_TRUE(VerifySingleBrowserFrame(browser, focused_frame, expected_url)); size_t frame_count = browser->GetFrameCount(); 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()); } // Names may be empty for callbacks that execute while the frame is loading. 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()); } V_RETURN(); } // Test that single navigation works. class FrameNavExpectationsBrowserTestSingleNav : public FrameNavExpectationsBrowserSingleNav { public: typedef FrameNavExpectationsBrowserSingleNav parent; explicit FrameNavExpectationsBrowserTestSingleNav(int nav) : parent(nav) {} std::string GetMainURL() override { return kFrameNavOrigin0; } std::string GetContentForURL(const std::string& url) override { return "Nav"; } bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames( browser, NULL, isLoading ? std::string() : kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } bool OnAfterCreated(CefRefPtr browser) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, std::string())); V_EXPECT_TRUE(parent::OnAfterCreated(browser)); V_RETURN(); } bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, const std::string& url) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, std::string())); V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame, url)); V_RETURN(); } bool GetResourceHandler(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, std::string())); V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame)); V_RETURN(); } bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, int renderer_nav, bool renderer_result) override { return parent::OnRendererComplete(browser, frame, renderer_nav, renderer_result); } bool Finalize() override { return parent::Finalize(); } }; class FrameNavExpectationsRendererTestSingleNav : public FrameNavExpectationsRendererSingleNav { public: typedef FrameNavExpectationsRendererSingleNav parent; explicit FrameNavExpectationsRendererTestSingleNav(int nav) : parent(nav) {} bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { V_DECLARE(); // A frame should always exist in the renderer process. V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, kFrameNavOrigin0)); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } bool Finalize() override { return parent::Finalize(); } }; class FrameNavExpectationsFactoryBrowserTestSingleNav : public FrameNavExpectationsFactoryBrowser { public: FrameNavExpectationsFactoryBrowserTestSingleNav() {} FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV; } bool HasMoreNavigations() const override { return false; } bool Finalize() override { return true; } protected: scoped_ptr Create(int nav) override { return scoped_ptr( new FrameNavExpectationsBrowserTestSingleNav(nav)); } }; class FrameNavExpectationsFactoryRendererTestSingleNav : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestSingleNav() {} FrameNavFactoryId GetID() const override { return FNF_ID_SINGLE_NAV; } protected: scoped_ptr Create(int nav) override { return 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) {} ~FrameNavExpectationsBrowserMultiNav() override { EXPECT_TRUE(got_finalize_); } // Returns true if all navigation is done. virtual bool IsNavigationDone() const = 0; bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { if (!isLoading) SignalCompleteIfDone(browser, browser->GetMainFrame()); return true; } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { SignalCompleteIfDone(browser, frame); return true; } bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, int renderer_nav, bool renderer_result) override { EXPECT_TRUE(renderer_result); SignalCompleteIfDone(browser, frame); return true; } bool Finalize() override { V_DECLARE(); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser, CefRefPtr frame) { if (IsNavigationDone()) SignalComplete(browser, frame); } TrackCallback got_finalize_; }; // Renderer process expectations for a multiple navigations. class FrameNavExpectationsRendererMultiNav : public FrameNavExpectationsRenderer { public: explicit FrameNavExpectationsRendererMultiNav(int nav) : FrameNavExpectationsRenderer(nav) {} ~FrameNavExpectationsRendererMultiNav() override { EXPECT_TRUE(got_finalize_); } // Returns true if all navigation is done. virtual bool IsNavigationDone() const = 0; bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { if (!isLoading) SignalCompleteIfDone(browser, browser->GetMainFrame()); return true; } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { SignalCompleteIfDone(browser, frame); return true; } bool Finalize() override { V_DECLARE(); V_EXPECT_FALSE(got_finalize_); got_finalize_.yes(); V_RETURN(); } private: void SignalCompleteIfDone(CefRefPtr browser, CefRefPtr frame) { if (IsNavigationDone()) SignalComplete(browser, frame); } 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) {} ~FrameNavExpectationsBrowserTestMultiNavHarness() override { EXPECT_TRUE(got_finalize_); } std::string GetMainURL() override { EXPECT_FALSE(got_get_main_url_); got_get_main_url_.yes(); return GetMultiNavURL(kFrameNavOrigin0, nav()); } 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"; } bool IsNavigationDone() const override { navigation_done_count_++; return got_load_state_change_done_ && got_load_end_ && got_renderer_complete_; } 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); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); return parent::OnLoadEnd(browser, frame); } bool OnAfterCreated(CefRefPtr browser) override { EXPECT_FALSE(got_on_after_created_); got_on_after_created_.yes(); return parent::OnAfterCreated(browser); } bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, 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, frame, renderer_nav, renderer_result); } 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) {} ~FrameNavExpectationsRendererTestMultiNavHarness() override { EXPECT_TRUE(got_finalize_); } bool IsNavigationDone() const override { navigation_done_count_++; return got_load_state_change_done_ && got_load_end_; } 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); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { EXPECT_FALSE(got_load_end_); got_load_end_.yes(); return parent::OnLoadEnd(browser, frame); } 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) {} ~FrameNavExpectationsFactoryBrowserTestMultiNavHarness() override { EXPECT_TRUE(got_finalize_); } FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV_HARNESS; } bool HasMoreNavigations() const override { get_browser_navigation_count_++; return (get_browser_navigation_count_ < kMaxMultiNavNavigations); } 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: scoped_ptr Create(int nav) override { create_count_++; return scoped_ptr( new FrameNavExpectationsBrowserTestMultiNavHarness(nav)); } private: mutable int get_browser_navigation_count_; int create_count_; TrackCallback got_finalize_; }; class FrameNavExpectationsFactoryRendererTestMultiNavHarness : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestMultiNavHarness() {} FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV_HARNESS; } protected: scoped_ptr Create(int nav) override { return 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) {} std::string GetMainURL() override { return GetMultiNavURL(kFrameNavOrigin0, nav()); } std::string GetContentForURL(const std::string& url) override { return "Nav"; } bool IsNavigationDone() const override { return got_load_state_change_done_ && got_load_end_ && got_renderer_complete_; } bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { if (!isLoading) got_load_state_change_done_.yes(); V_DECLARE(); if (isLoading && nav() == 0) { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, std::string())); } else if (isLoading) { // Expect the URL from the previous load. V_EXPECT_TRUE( VerifySingleBrowserFrames(browser, NULL, GetPreviousMainURL())); } else { V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, GetMainURL())); } V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { got_load_end_.yes(); V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } bool OnAfterCreated(CefRefPtr browser) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, std::string())); V_EXPECT_TRUE(parent::OnAfterCreated(browser)); V_RETURN(); } bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, const std::string& url) override { V_DECLARE(); std::string expected_url; if (nav() > 0) expected_url = GetPreviousMainURL(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, expected_url)); V_EXPECT_TRUE(parent::OnBeforeBrowse(browser, frame, url)); V_RETURN(); } 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, expected_url)); V_EXPECT_TRUE(parent::GetResourceHandler(browser, frame)); V_RETURN(); } bool OnRendererComplete(CefRefPtr browser, CefRefPtr frame, 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, frame, renderer_nav, renderer_result)); V_RETURN(); } 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) {} bool IsNavigationDone() const override { return got_load_state_change_done_ && got_load_end_; } bool OnLoadingStateChange(CefRefPtr browser, bool isLoading) override { if (!isLoading) got_load_state_change_done_.yes(); V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, NULL, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadingStateChange(browser, isLoading)); V_RETURN(); } bool OnLoadStart(CefRefPtr browser, CefRefPtr frame) override { V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadStart(browser, frame)); V_RETURN(); } bool OnLoadEnd(CefRefPtr browser, CefRefPtr frame) override { got_load_end_.yes(); V_DECLARE(); V_EXPECT_TRUE(VerifySingleBrowserFrames(browser, frame, GetMainURL())); V_EXPECT_TRUE(parent::OnLoadEnd(browser, frame)); V_RETURN(); } 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) {} FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV; } bool HasMoreNavigations() const override { return (nav_count_ < kMaxMultiNavNavigations); } bool Finalize() override { V_DECLARE(); V_EXPECT_TRUE(nav_count_ == kMaxMultiNavNavigations); V_RETURN(); } protected: scoped_ptr Create(int nav) override { nav_count_++; return scoped_ptr( new FrameNavExpectationsBrowserTestMultiNav(nav)); } private: int nav_count_; }; class FrameNavExpectationsFactoryRendererTestMultiNav : public FrameNavExpectationsFactoryRenderer { public: FrameNavExpectationsFactoryRendererTestMultiNav() {} FrameNavFactoryId GetID() const override { return FNF_ID_MULTI_NAV; } protected: scoped_ptr Create(int nav) override { return 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[] = "-->"; const char kFrame3Name[] = "nav3"; bool VerifyBrowserIframe(CefRefPtr browser, CefRefPtr frame, const std::string& origin, int frame_number) { V_DECLARE(); // frame0 contains frame1 contains frame2, contains frame3. CefRefPtr frame0, frame1, frame2, frame3; CefRefPtr frame0b, frame1b, frame2b, frame3b; int64 frame0id, frame1id, frame2id, frame3id; std::string frame0url, frame1url, frame2url, frame3url; // Verify the GetFrameNames result. std::set expected_names = {kFrame0Name, kFrame1Name, kFrame2Name, kFrame3Name}; std::vector names; browser->GetFrameNames(names); V_EXPECT_TRUE(names.size() == expected_names.size()) << "expected: " << expected_names.size() << " actual: " << names.size(); for (const auto& name : names) { const std::string& nameStr = name; auto it = expected_names.find(nameStr); V_EXPECT_FALSE(it == expected_names.end()) << "Unexpected name: \"" << nameStr << "\""; if (it != expected_names.end()) expected_names.erase(it); } for (const auto& name : expected_names) { V_EXPECT_FALSE(true) << "Missing name: \"" << name << "\""; } // 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()); frame3 = browser->GetFrame(kFrame3Name); V_EXPECT_TRUE(frame3.get()); if (!frame0 || !frame1 || !frame2 || !frame3) { V_RETURN(); } // Verify that the name matches. V_EXPECT_TRUE(frame0->GetName().ToString() == kFrame0Name) << "expected: " << kFrame0Name << " actual: " << frame0->GetName().ToString(); V_EXPECT_TRUE(frame1->GetName().ToString() == kFrame1Name) << "expected: " << kFrame1Name << " actual: " << frame1->GetName().ToString(); V_EXPECT_TRUE(frame2->GetName().ToString() == kFrame2Name) << "expected: " << kFrame2Name << " actual: " << frame2->GetName().ToString(); V_EXPECT_TRUE(frame3->GetName().ToString() == kFrame3Name) << "expected: " << kFrame3Name << " actual: " << frame3->GetName().ToString(); // Verify that the URL matches. frame0url = GetMultiNavURL(origin, 0); V_EXPECT_TRUE(frame0->GetURL() == frame0url) << "expected: " << frame0url << " actual: " << frame0->GetURL().ToString(); frame1url = GetMultiNavURL(origin, 1); V_EXPECT_TRUE(frame1->GetURL() == frame1url) << "expected: " << frame1url << " actual: " << frame1->GetURL().ToString(); frame2url = GetMultiNavURL(origin, 2); V_EXPECT_TRUE(frame2->GetURL() == frame2url) << "expected: " << frame2url << " actual: " << frame2->GetURL().ToString(); frame3url = GetMultiNavURL(origin, 3); V_EXPECT_TRUE(frame3->GetURL() == frame3url) << "expected: " << frame3url << " actual: " << frame3->GetURL().ToString(); // Verify that the frame id is valid. frame0id = frame0->GetIdentifier(); V_EXPECT_TRUE(frame0id > 0) << "actual: " << frame0id; frame1id = frame1->GetIdentifier(); V_EXPECT_TRUE(frame1id > 0) << "actual: " << frame1id; frame2id = frame2->GetIdentifier(); V_EXPECT_TRUE(frame2id > 0) << "actual: " << frame2id; frame3id = frame3->GetIdentifier(); V_EXPECT_TRUE(frame3id > 0) << "actual: " << frame3id; // Verify that the current frame has the correct id. if (frame_number == 0) { V_EXPECT_TRUE(frame->GetIdentifier() == frame0id) << "expected: " << frame0id << " actual: " << frame->GetIdentifier(); } else if (frame_number == 1) { V_EXPECT_TRUE(frame->GetIdentifier() == frame1id) << "expected: " << frame1id << " actual: " << frame->GetIdentifier(); } else if (frame_number == 2) { V_EXPECT_TRUE(frame->GetIdentifier() == frame2id) << "expected: " << frame2id << " actual: " << frame->GetIdentifier(); } else if (frame_number == 3) { V_EXPECT_TRUE(frame->GetIdentifier() == frame3id) << "expected: " << frame3id << " actual: " << frame->GetIdentifier(); } // 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()); frame3b = browser->GetFrame(frame3->GetIdentifier()); V_EXPECT_TRUE(frame3b.get()); if (!frame0b || !frame1b || !frame2b || !frame3b) { V_RETURN(); } // Verify that the id matches. V_EXPECT_TRUE(frame0b->GetIdentifier() == frame0id) << "expected: " << frame0id << " actual: " << frame0b->GetIdentifier(); V_EXPECT_TRUE(frame1b->GetIdentifier() == frame1id) << "expected: " << frame1id << " actual: " << frame1b->GetIdentifier(); V_EXPECT_TRUE(frame2b->GetIdentifier() == frame2id) << "expected: " << frame2id << " actual: " << frame2b->GetIdentifier(); V_EXPECT_TRUE(frame3b->GetIdentifier() == frame3id) << "expected: " << frame3id << " actual: " << frame3b->GetIdentifier(); size_t frame_count = browser->GetFrameCount(); V_EXPECT_TRUE(frame_count == 4U) << " actual: " << frame_count; // Verify the GetFrameIdentifiers result. std::set expected_idents = {frame0id, frame1id, frame2id, frame3id}; std::vector idents; browser->GetFrameIdentifiers(idents); V_EXPECT_TRUE(idents.size() == expected_idents.size()) << "expected: " << expected_idents.size() << " actual: " << idents.size(); for (const auto& ident : idents) { auto it = expected_idents.find(ident); V_EXPECT_FALSE(it == expected_idents.end()) << "Unexpected id: " << ident; if (it != expected_idents.end()) expected_idents.erase(it); } for (const auto& ident : expected_idents) { V_EXPECT_FALSE(true) << "Missing id: " << ident; } // Verify parent hierarchy. V_EXPECT_FALSE(frame0->GetParent().get()); V_EXPECT_TRUE(frame1->GetParent()->GetIdentifier() == frame0id) << "expected: " << frame0id << " actual: " << frame1->GetParent()->GetIdentifier(); V_EXPECT_TRUE(frame2->GetParent()->GetIdentifier() == frame1id) << "expected: " << frame1id << " actual: " << frame2->GetParent()->GetIdentifier(); V_EXPECT_TRUE(frame3->GetParent()->GetIdentifier() == frame2id) << "expected: " << frame2id << " actual: " << frame3->GetParent()->GetIdentifier(); 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; case 3: origin_ = kFrameNavOrigin3; break; default: EXPECT_TRUE(false); // Not reached. break; } } } std::string GetMainURL() override { // Load the first (main) frame. return GetMultiNavURL(origin_, 0); } 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