| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "content/browser/frame_host/navigation_entry_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/browser/web_contents/web_contents_view.h" |
| #include "content/common/frame_messages.h" |
| #include "content/public/browser/load_notification_details.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/content_paths.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| namespace content { |
| |
| void ResizeWebContentsView(Shell* shell, const gfx::Size& size, |
| bool set_start_page) { |
| // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents |
| // works on Win and ChromeOS but not Linux - we need to resize the shell |
| // window on Linux because if we don't, the next layout of the unchanged shell |
| // window will resize WebContentsView back to the previous size. |
| // SizeContents is a hack and should not be relied on. |
| #if defined(OS_MACOSX) |
| shell->SizeTo(size); |
| // If |set_start_page| is true, start with blank page to make sure resize |
| // takes effect. |
| if (set_start_page) |
| NavigateToURL(shell, GURL("about://blank")); |
| #else |
| static_cast<WebContentsImpl*>(shell->web_contents())->GetView()-> |
| SizeContents(size); |
| #endif // defined(OS_MACOSX) |
| } |
| |
| class WebContentsImplBrowserTest : public ContentBrowserTest { |
| public: |
| WebContentsImplBrowserTest() {} |
| void SetUp() override { |
| RenderWidgetHostImpl::DisableResizeAckCheckForTesting(); |
| ContentBrowserTest::SetUp(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(WebContentsImplBrowserTest); |
| }; |
| |
| // Keeps track of data from LoadNotificationDetails so we can later verify that |
| // they are correct, after the LoadNotificationDetails object is deleted. |
| class LoadStopNotificationObserver : public WindowedNotificationObserver { |
| public: |
| LoadStopNotificationObserver(NavigationController* controller) |
| : WindowedNotificationObserver(NOTIFICATION_LOAD_STOP, |
| Source<NavigationController>(controller)), |
| session_index_(-1), |
| controller_(NULL) { |
| } |
| void Observe(int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) override { |
| if (type == NOTIFICATION_LOAD_STOP) { |
| const Details<LoadNotificationDetails> load_details(details); |
| url_ = load_details->url; |
| session_index_ = load_details->session_index; |
| controller_ = load_details->controller; |
| } |
| WindowedNotificationObserver::Observe(type, source, details); |
| } |
| |
| GURL url_; |
| int session_index_; |
| NavigationController* controller_; |
| }; |
| |
| // Starts a new navigation as soon as the current one commits, but does not |
| // wait for it to complete. This allows us to observe DidStopLoading while |
| // a pending entry is present. |
| class NavigateOnCommitObserver : public WebContentsObserver { |
| public: |
| NavigateOnCommitObserver(Shell* shell, GURL url) |
| : WebContentsObserver(shell->web_contents()), |
| shell_(shell), |
| url_(url), |
| done_(false) { |
| } |
| |
| // WebContentsObserver: |
| void NavigationEntryCommitted( |
| const LoadCommittedDetails& load_details) override { |
| if (!done_) { |
| done_ = true; |
| shell_->Stop(); |
| shell_->LoadURL(url_); |
| } |
| } |
| |
| Shell* shell_; |
| GURL url_; |
| bool done_; |
| }; |
| |
| class RenderViewSizeDelegate : public WebContentsDelegate { |
| public: |
| void set_size_insets(const gfx::Size& size_insets) { |
| size_insets_ = size_insets; |
| } |
| |
| // WebContentsDelegate: |
| gfx::Size GetSizeForNewRenderView(WebContents* web_contents) const override { |
| gfx::Size size(web_contents->GetContainerBounds().size()); |
| size.Enlarge(size_insets_.width(), size_insets_.height()); |
| return size; |
| } |
| |
| private: |
| gfx::Size size_insets_; |
| }; |
| |
| class RenderViewSizeObserver : public WebContentsObserver { |
| public: |
| RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size) |
| : WebContentsObserver(shell->web_contents()), |
| shell_(shell), |
| wcv_new_size_(wcv_new_size) { |
| } |
| |
| // WebContentsObserver: |
| void RenderViewCreated(RenderViewHost* rvh) override { |
| rwhv_create_size_ = rvh->GetWidget()->GetView()->GetViewBounds().size(); |
| } |
| |
| void DidStartProvisionalLoadForFrame( |
| RenderFrameHost* render_frame_host, |
| const GURL& url, |
| bool is_error_page, |
| bool is_iframe_srcdoc) override { |
| ResizeWebContentsView(shell_, wcv_new_size_, false); |
| } |
| |
| gfx::Size rwhv_create_size() const { return rwhv_create_size_; } |
| |
| private: |
| Shell* shell_; // Weak ptr. |
| gfx::Size wcv_new_size_; |
| gfx::Size rwhv_create_size_; |
| }; |
| |
| class LoadingStateChangedDelegate : public WebContentsDelegate { |
| public: |
| LoadingStateChangedDelegate() |
| : loadingStateChangedCount_(0) |
| , loadingStateToDifferentDocumentCount_(0) { |
| } |
| |
| // WebContentsDelegate: |
| void LoadingStateChanged(WebContents* contents, |
| bool to_different_document) override { |
| loadingStateChangedCount_++; |
| if (to_different_document) |
| loadingStateToDifferentDocumentCount_++; |
| } |
| |
| int loadingStateChangedCount() const { return loadingStateChangedCount_; } |
| int loadingStateToDifferentDocumentCount() const { |
| return loadingStateToDifferentDocumentCount_; |
| } |
| |
| private: |
| int loadingStateChangedCount_; |
| int loadingStateToDifferentDocumentCount_; |
| }; |
| |
| // Test that a renderer-initiated navigation to an invalid URL does not leave |
| // around a pending entry that could be used in a URL spoof. We test this in |
| // a browser test because our unit test framework incorrectly calls |
| // DidStartProvisionalLoadForFrame for in-page navigations. |
| // See http://crbug.com/280512. |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| ClearNonVisiblePendingOnFail) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| |
| // Navigate to an invalid URL and make sure it doesn't leave a pending entry. |
| LoadStopNotificationObserver load_observer1( |
| &shell()->web_contents()->GetController()); |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.location.href=\"nonexistent:12121\";")); |
| load_observer1.Wait(); |
| EXPECT_FALSE(shell()->web_contents()->GetController().GetPendingEntry()); |
| |
| LoadStopNotificationObserver load_observer2( |
| &shell()->web_contents()->GetController()); |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.location.href=\"#foo\";")); |
| load_observer2.Wait(); |
| EXPECT_EQ(embedded_test_server()->GetURL("/title1.html#foo"), |
| shell()->web_contents()->GetVisibleURL()); |
| } |
| |
| // Crashes under ThreadSanitizer, http://crbug.com/356758. |
| #if defined(OS_WIN) || defined(OS_ANDROID) \ |
| || defined(THREAD_SANITIZER) |
| #define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView |
| #else |
| #define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView |
| #endif |
| // Test that RenderViewHost is created and updated at the size specified by |
| // WebContentsDelegate::GetSizeForNewRenderView(). |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| MAYBE_GetSizeForNewRenderView) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| // Create a new server with a different site. |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.ServeFilesFromSourceDirectory("content/test/data"); |
| ASSERT_TRUE(https_server.Start()); |
| |
| scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate()); |
| shell()->web_contents()->SetDelegate(delegate.get()); |
| ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get()); |
| |
| // When no size is set, RenderWidgetHostView adopts the size of |
| // WebContentsView. |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")); |
| EXPECT_EQ(shell()->web_contents()->GetContainerBounds().size(), |
| shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds(). |
| size()); |
| |
| // When a size is set, RenderWidgetHostView and WebContentsView honor this |
| // size. |
| gfx::Size size(300, 300); |
| gfx::Size size_insets(10, 15); |
| ResizeWebContentsView(shell(), size, true); |
| delegate->set_size_insets(size_insets); |
| NavigateToURL(shell(), https_server.GetURL("/")); |
| size.Enlarge(size_insets.width(), size_insets.height()); |
| EXPECT_EQ(size, |
| shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds(). |
| size()); |
| // The web_contents size is set by the embedder, and should not depend on the |
| // rwhv size. The behavior is correct on OSX, but incorrect on other |
| // platforms. |
| gfx::Size exp_wcv_size(300, 300); |
| #if !defined(OS_MACOSX) |
| exp_wcv_size.Enlarge(size_insets.width(), size_insets.height()); |
| #endif |
| |
| EXPECT_EQ(exp_wcv_size, |
| shell()->web_contents()->GetContainerBounds().size()); |
| |
| // If WebContentsView is resized after RenderWidgetHostView is created but |
| // before pending navigation entry is committed, both RenderWidgetHostView and |
| // WebContentsView use the new size of WebContentsView. |
| gfx::Size init_size(200, 200); |
| gfx::Size new_size(100, 100); |
| size_insets = gfx::Size(20, 30); |
| ResizeWebContentsView(shell(), init_size, true); |
| delegate->set_size_insets(size_insets); |
| RenderViewSizeObserver observer(shell(), new_size); |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| // RenderWidgetHostView is created at specified size. |
| init_size.Enlarge(size_insets.width(), size_insets.height()); |
| EXPECT_EQ(init_size, observer.rwhv_create_size()); |
| |
| // Once again, the behavior is correct on OSX. The embedder explicitly sets |
| // the size to (100,100) during navigation. Both the wcv and the rwhv should |
| // take on that size. |
| #if !defined(OS_MACOSX) |
| new_size.Enlarge(size_insets.width(), size_insets.height()); |
| #endif |
| gfx::Size actual_size = shell()->web_contents()->GetRenderWidgetHostView()-> |
| GetViewBounds().size(); |
| |
| EXPECT_EQ(new_size, actual_size); |
| EXPECT_EQ(new_size, shell()->web_contents()->GetContainerBounds().size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, SetTitleOnUnload) { |
| GURL url( |
| "data:text/html," |
| "<title>A</title>" |
| "<body onunload=\"document.title = 'B'\"></body>"); |
| NavigateToURL(shell(), url); |
| ASSERT_EQ(1, shell()->web_contents()->GetController().GetEntryCount()); |
| NavigationEntryImpl* entry1 = NavigationEntryImpl::FromNavigationEntry( |
| shell()->web_contents()->GetController().GetLastCommittedEntry()); |
| SiteInstance* site_instance1 = entry1->site_instance(); |
| EXPECT_EQ(base::ASCIIToUTF16("A"), entry1->GetTitle()); |
| |
| // Force a process switch by going to a privileged page. |
| GURL web_ui_page(std::string(kChromeUIScheme) + "://" + |
| std::string(kChromeUIGpuHost)); |
| NavigateToURL(shell(), web_ui_page); |
| NavigationEntryImpl* entry2 = NavigationEntryImpl::FromNavigationEntry( |
| shell()->web_contents()->GetController().GetLastCommittedEntry()); |
| SiteInstance* site_instance2 = entry2->site_instance(); |
| EXPECT_NE(site_instance1, site_instance2); |
| |
| EXPECT_EQ(2, shell()->web_contents()->GetController().GetEntryCount()); |
| EXPECT_EQ(base::ASCIIToUTF16("B"), entry1->GetTitle()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, OpenURLSubframe) { |
| // Navigate to a page with frames and grab a subframe's FrameTreeNode ID. |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| NavigateToURL(shell(), |
| embedded_test_server()->GetURL("/frame_tree/top.html")); |
| WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
| FrameTreeNode* root = wc->GetFrameTree()->root(); |
| ASSERT_EQ(3UL, root->child_count()); |
| int frame_tree_node_id = root->child_at(0)->frame_tree_node_id(); |
| EXPECT_NE(-1, frame_tree_node_id); |
| |
| // Navigate with the subframe's FrameTreeNode ID. |
| const GURL url(embedded_test_server()->GetURL("/title1.html")); |
| OpenURLParams params(url, Referrer(), frame_tree_node_id, CURRENT_TAB, |
| ui::PAGE_TRANSITION_LINK, true); |
| shell()->web_contents()->OpenURL(params); |
| |
| // Make sure the NavigationEntry ends up with the FrameTreeNode ID. |
| NavigationController* controller = &shell()->web_contents()->GetController(); |
| EXPECT_TRUE(controller->GetPendingEntry()); |
| EXPECT_EQ(frame_tree_node_id, |
| NavigationEntryImpl::FromNavigationEntry( |
| controller->GetPendingEntry())->frame_tree_node_id()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| AppendingFrameInWebUIDoesNotCrash) { |
| const GURL kWebUIUrl("chrome://tracing"); |
| const char kJSCodeForAppendingFrame[] = |
| "document.body.appendChild(document.createElement('iframe'));"; |
| |
| NavigateToURL(shell(), kWebUIUrl); |
| |
| bool js_executed = content::ExecuteScript(shell()->web_contents(), |
| kJSCodeForAppendingFrame); |
| EXPECT_TRUE(js_executed); |
| } |
| |
| // Observer class to track the creation of RenderFrameHost objects. It is used |
| // in subsequent tests. |
| class RenderFrameCreatedObserver : public WebContentsObserver { |
| public: |
| RenderFrameCreatedObserver(Shell* shell) |
| : WebContentsObserver(shell->web_contents()), |
| last_rfh_(NULL) { |
| } |
| |
| void RenderFrameCreated(RenderFrameHost* render_frame_host) override { |
| last_rfh_ = render_frame_host; |
| } |
| |
| RenderFrameHost* last_rfh() const { return last_rfh_; } |
| |
| private: |
| RenderFrameHost* last_rfh_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RenderFrameCreatedObserver); |
| }; |
| |
| // Test that creation of new RenderFrameHost objects sends the correct object |
| // to the WebContentObservers. See http://crbug.com/347339. |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| RenderFrameCreatedCorrectProcessForObservers) { |
| static const char kFooCom[] = "foo.com"; |
| GURL::Replacements replace_host; |
| net::HostPortPair foo_host_port; |
| GURL cross_site_url; |
| |
| // Setup the server to allow serving separate sites, so we can perform |
| // cross-process navigation. |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| foo_host_port = embedded_test_server()->host_port_pair(); |
| foo_host_port.set_host(kFooCom); |
| |
| GURL initial_url(embedded_test_server()->GetURL("/title1.html")); |
| |
| cross_site_url = embedded_test_server()->GetURL("/title2.html"); |
| replace_host.SetHostStr(kFooCom); |
| cross_site_url = cross_site_url.ReplaceComponents(replace_host); |
| |
| // Navigate to the initial URL and capture the RenderFrameHost for later |
| // comparison. |
| NavigateToURL(shell(), initial_url); |
| RenderFrameHost* orig_rfh = shell()->web_contents()->GetMainFrame(); |
| |
| // Install the observer and navigate cross-site. |
| RenderFrameCreatedObserver observer(shell()); |
| NavigateToURL(shell(), cross_site_url); |
| |
| // The observer should've seen a RenderFrameCreated call for the new frame |
| // and not the old one. |
| EXPECT_NE(observer.last_rfh(), orig_rfh); |
| EXPECT_EQ(observer.last_rfh(), shell()->web_contents()->GetMainFrame()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| LoadingStateChangedForSameDocumentNavigation) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| scoped_ptr<LoadingStateChangedDelegate> delegate( |
| new LoadingStateChangedDelegate()); |
| shell()->web_contents()->SetDelegate(delegate.get()); |
| |
| LoadStopNotificationObserver load_observer( |
| &shell()->web_contents()->GetController()); |
| TitleWatcher title_watcher(shell()->web_contents(), |
| base::ASCIIToUTF16("pushState")); |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/push_state.html")); |
| load_observer.Wait(); |
| base::string16 title = title_watcher.WaitAndGetTitle(); |
| ASSERT_EQ(title, base::ASCIIToUTF16("pushState")); |
| |
| // LoadingStateChanged should be called 4 times: start and stop for the |
| // initial load of push_state.html, and start and stop for the "navigation" |
| // triggered by history.pushState(). However, the start notification for the |
| // history.pushState() navigation should set to_different_document to false. |
| EXPECT_EQ("pushState", shell()->web_contents()->GetLastCommittedURL().ref()); |
| EXPECT_EQ(4, delegate->loadingStateChangedCount()); |
| EXPECT_EQ(3, delegate->loadingStateToDifferentDocumentCount()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| RenderViewCreatedForChildWindow) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| NavigateToURL(shell(), |
| embedded_test_server()->GetURL("/title1.html")); |
| |
| WebContentsAddedObserver new_web_contents_observer; |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), |
| "var a = document.createElement('a');" |
| "a.href='./title2.html';" |
| "a.target = '_blank';" |
| "document.body.appendChild(a);" |
| "a.click();")); |
| WebContents* new_web_contents = new_web_contents_observer.GetWebContents(); |
| WaitForLoadStop(new_web_contents); |
| EXPECT_TRUE(new_web_contents_observer.RenderViewCreatedCalled()); |
| } |
| |
| struct LoadProgressDelegateAndObserver : public WebContentsDelegate, |
| public WebContentsObserver { |
| LoadProgressDelegateAndObserver(Shell* shell) |
| : WebContentsObserver(shell->web_contents()), |
| did_start_loading(false), |
| did_stop_loading(false) { |
| web_contents()->SetDelegate(this); |
| } |
| |
| // WebContentsDelegate: |
| void LoadProgressChanged(WebContents* source, double progress) override { |
| EXPECT_TRUE(did_start_loading); |
| EXPECT_FALSE(did_stop_loading); |
| progresses.push_back(progress); |
| } |
| |
| // WebContentsObserver: |
| void DidStartLoading() override { |
| EXPECT_FALSE(did_start_loading); |
| EXPECT_EQ(0U, progresses.size()); |
| EXPECT_FALSE(did_stop_loading); |
| did_start_loading = true; |
| } |
| |
| void DidStopLoading() override { |
| EXPECT_TRUE(did_start_loading); |
| EXPECT_GE(progresses.size(), 1U); |
| EXPECT_FALSE(did_stop_loading); |
| did_stop_loading = true; |
| } |
| |
| bool did_start_loading; |
| std::vector<double> progresses; |
| bool did_stop_loading; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, LoadProgress) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| scoped_ptr<LoadProgressDelegateAndObserver> delegate( |
| new LoadProgressDelegateAndObserver(shell())); |
| |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| |
| const std::vector<double>& progresses = delegate->progresses; |
| // All updates should be in order ... |
| if (std::adjacent_find(progresses.begin(), |
| progresses.end(), |
| std::greater<double>()) != progresses.end()) { |
| ADD_FAILURE() << "Progress values should be in order: " |
| << ::testing::PrintToString(progresses); |
| } |
| |
| // ... and the last one should be 1.0, meaning complete. |
| ASSERT_GE(progresses.size(), 1U) |
| << "There should be at least one progress update"; |
| EXPECT_EQ(1.0, *progresses.rbegin()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, LoadProgressWithFrames) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| scoped_ptr<LoadProgressDelegateAndObserver> delegate( |
| new LoadProgressDelegateAndObserver(shell())); |
| |
| NavigateToURL(shell(), |
| embedded_test_server()->GetURL("/frame_tree/top.html")); |
| |
| const std::vector<double>& progresses = delegate->progresses; |
| // All updates should be in order ... |
| if (std::adjacent_find(progresses.begin(), |
| progresses.end(), |
| std::greater<double>()) != progresses.end()) { |
| ADD_FAILURE() << "Progress values should be in order: " |
| << ::testing::PrintToString(progresses); |
| } |
| |
| // ... and the last one should be 1.0, meaning complete. |
| ASSERT_GE(progresses.size(), 1U) |
| << "There should be at least one progress update"; |
| EXPECT_EQ(1.0, *progresses.rbegin()); |
| } |
| |
| // Ensure that a new navigation that interrupts a pending one will still fire |
| // a DidStopLoading. See http://crbug.com/429399. |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| LoadProgressAfterInterruptedNav) { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Start at a real page. |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| |
| // Simulate a navigation that has not completed. |
| scoped_ptr<LoadProgressDelegateAndObserver> delegate( |
| new LoadProgressDelegateAndObserver(shell())); |
| RenderFrameHost* main_frame = shell()->web_contents()->GetMainFrame(); |
| FrameHostMsg_DidStartLoading start_msg(main_frame->GetRoutingID(), true); |
| static_cast<RenderFrameHostImpl*>(main_frame)->OnMessageReceived(start_msg); |
| EXPECT_TRUE(delegate->did_start_loading); |
| EXPECT_FALSE(delegate->did_stop_loading); |
| |
| // Also simulate a DidChangeLoadProgress, but not a DidStopLoading. |
| FrameHostMsg_DidChangeLoadProgress progress_msg(main_frame->GetRoutingID(), |
| 1.0); |
| static_cast<RenderFrameHostImpl*>(main_frame)->OnMessageReceived( |
| progress_msg); |
| EXPECT_TRUE(delegate->did_start_loading); |
| EXPECT_FALSE(delegate->did_stop_loading); |
| |
| // Now interrupt with a new cross-process navigation. |
| TestNavigationObserver tab_observer(shell()->web_contents(), 1); |
| GURL url(embedded_test_server()->GetURL("foo.com", "/title2.html")); |
| shell()->LoadURL(url); |
| tab_observer.Wait(); |
| EXPECT_EQ(url, shell()->web_contents()->GetLastCommittedURL()); |
| |
| // We should have gotten to DidStopLoading. |
| EXPECT_TRUE(delegate->did_stop_loading); |
| } |
| |
| struct FirstVisuallyNonEmptyPaintObserver : public WebContentsObserver { |
| FirstVisuallyNonEmptyPaintObserver(Shell* shell) |
| : WebContentsObserver(shell->web_contents()), |
| did_fist_visually_non_empty_paint_(false) {} |
| |
| void DidFirstVisuallyNonEmptyPaint() override { |
| did_fist_visually_non_empty_paint_ = true; |
| on_did_first_visually_non_empty_paint_.Run(); |
| } |
| |
| void WaitForDidFirstVisuallyNonEmptyPaint() { |
| if (did_fist_visually_non_empty_paint_) |
| return; |
| base::RunLoop run_loop; |
| on_did_first_visually_non_empty_paint_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| base::Closure on_did_first_visually_non_empty_paint_; |
| bool did_fist_visually_non_empty_paint_; |
| }; |
| |
| // See: http://crbug.com/395664 |
| #if defined(OS_ANDROID) |
| #define MAYBE_FirstVisuallyNonEmptyPaint DISABLED_FirstVisuallyNonEmptyPaint |
| #else |
| // http://crbug.com/398471 |
| #define MAYBE_FirstVisuallyNonEmptyPaint DISABLED_FirstVisuallyNonEmptyPaint |
| #endif |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, |
| MAYBE_FirstVisuallyNonEmptyPaint) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| scoped_ptr<FirstVisuallyNonEmptyPaintObserver> observer( |
| new FirstVisuallyNonEmptyPaintObserver(shell())); |
| |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| |
| observer->WaitForDidFirstVisuallyNonEmptyPaint(); |
| ASSERT_TRUE(observer->did_fist_visually_non_empty_paint_); |
| } |
| |
| namespace { |
| |
| class WebDisplayModeDelegate : public WebContentsDelegate { |
| public: |
| explicit WebDisplayModeDelegate(blink::WebDisplayMode mode) : mode_(mode) { } |
| ~WebDisplayModeDelegate() override { } |
| |
| blink::WebDisplayMode GetDisplayMode( |
| const WebContents* source) const override { return mode_; } |
| void set_mode(blink::WebDisplayMode mode) { mode_ = mode; } |
| private: |
| blink::WebDisplayMode mode_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebDisplayModeDelegate); |
| }; |
| |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ChangeDisplayMode) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| WebDisplayModeDelegate delegate(blink::WebDisplayModeMinimalUi); |
| shell()->web_contents()->SetDelegate(&delegate); |
| |
| NavigateToURL(shell(), GURL("about://blank")); |
| |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), |
| "document.title = " |
| " window.matchMedia('(display-mode:" |
| " minimal-ui)').matches")); |
| EXPECT_EQ(base::ASCIIToUTF16("true"), shell()->web_contents()->GetTitle()); |
| |
| delegate.set_mode(blink::WebDisplayModeFullscreen); |
| // Simulate widget is entering fullscreen (changing size is enough). |
| shell()->web_contents()->GetRenderViewHost()->GetWidget()->WasResized(); |
| |
| ASSERT_TRUE(ExecuteScript(shell()->web_contents(), |
| "document.title = " |
| " window.matchMedia('(display-mode:" |
| " fullscreen)').matches")); |
| EXPECT_EQ(base::ASCIIToUTF16("true"), shell()->web_contents()->GetTitle()); |
| } |
| |
| // Observer class used to verify that WebContentsObservers are notified |
| // when the page scale factor changes. |
| // See WebContentsImplBrowserTest.ChangePageScale. |
| class MockPageScaleObserver : public WebContentsObserver { |
| public: |
| MockPageScaleObserver(Shell* shell) |
| : WebContentsObserver(shell->web_contents()), |
| got_page_scale_update_(false) { |
| // Once OnPageScaleFactorChanged is called, quit the run loop. |
| ON_CALL(*this, OnPageScaleFactorChanged(::testing::_)).WillByDefault( |
| ::testing::InvokeWithoutArgs( |
| this, &MockPageScaleObserver::GotPageScaleUpdate)); |
| } |
| |
| MOCK_METHOD1(OnPageScaleFactorChanged, void(float page_scale_factor)); |
| |
| void WaitForPageScaleUpdate() { |
| if (!got_page_scale_update_) { |
| base::RunLoop run_loop; |
| on_page_scale_update_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| got_page_scale_update_ = false; |
| } |
| |
| private: |
| void GotPageScaleUpdate() { |
| got_page_scale_update_ = true; |
| on_page_scale_update_.Run(); |
| } |
| |
| base::Closure on_page_scale_update_; |
| bool got_page_scale_update_; |
| }; |
| |
| // When the page scale factor is set in the renderer it should send |
| // a notification to the browser so that WebContentsObservers are notified. |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ChangePageScale) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); |
| |
| MockPageScaleObserver observer(shell()); |
| ::testing::InSequence expect_call_sequence; |
| |
| shell()->web_contents()->SetPageScale(1.5); |
| EXPECT_CALL(observer, OnPageScaleFactorChanged(::testing::FloatEq(1.5))); |
| observer.WaitForPageScaleUpdate(); |
| |
| // Navigate to reset the page scale factor. |
| shell()->LoadURL(embedded_test_server()->GetURL("/title2.html")); |
| EXPECT_CALL(observer, OnPageScaleFactorChanged(::testing::_)); |
| observer.WaitForPageScaleUpdate(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NewNamedWindow) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL url = embedded_test_server()->GetURL("/click-noreferrer-links.html"); |
| EXPECT_TRUE(NavigateToURL(shell(), url)); |
| |
| { |
| ShellAddedObserver new_shell_observer; |
| |
| // Open a new, named window. |
| EXPECT_TRUE(ExecuteScript(shell()->web_contents(), |
| "window.open('about:blank','new_window');")); |
| |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| EXPECT_EQ("new_window", |
| static_cast<WebContentsImpl*>(new_shell->web_contents()) |
| ->GetFrameTree()->root()->frame_name()); |
| |
| bool success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| new_shell->web_contents(), |
| "window.domAutomationController.send(window.name == 'new_window');", |
| &success)); |
| EXPECT_TRUE(success); |
| } |
| |
| { |
| ShellAddedObserver new_shell_observer; |
| |
| // Test clicking a target=foo link. |
| bool success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| shell()->web_contents(), |
| "window.domAutomationController.send(clickSameSiteTargetedLink());", |
| &success)); |
| EXPECT_TRUE(success); |
| |
| Shell* new_shell = new_shell_observer.GetShell(); |
| WaitForLoadStop(new_shell->web_contents()); |
| |
| EXPECT_EQ("foo", |
| static_cast<WebContentsImpl*>(new_shell->web_contents()) |
| ->GetFrameTree()->root()->frame_name()); |
| } |
| } |
| |
| } // namespace content |