| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/controls/webview/webview.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/mock_render_process_host.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "content/test/test_content_browser_client.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/views/controls/native/native_view_host.h" |
| #include "ui/views/test/test_views_delegate.h" |
| #include "ui/views/test/widget_test.h" |
| |
| #if defined(USE_AURA) |
| #include "ui/aura/window.h" |
| #endif |
| |
| namespace views { |
| |
| namespace { |
| |
| // Provides functionality to observe events on a WebContents like |
| // OnVisibilityChanged/WebContentsDestroyed. |
| class WebViewTestWebContentsObserver : public content::WebContentsObserver { |
| public: |
| explicit WebViewTestWebContentsObserver(content::WebContents* web_contents) |
| : web_contents_(web_contents), |
| was_shown_(false), |
| shown_count_(0), |
| hidden_count_(0), |
| valid_root_while_shown_(true) { |
| content::WebContentsObserver::Observe(web_contents); |
| } |
| |
| ~WebViewTestWebContentsObserver() override { |
| if (web_contents_) |
| content::WebContentsObserver::Observe(NULL); |
| } |
| |
| void WebContentsDestroyed() override { |
| DCHECK(web_contents_); |
| content::WebContentsObserver::Observe(NULL); |
| web_contents_ = NULL; |
| } |
| |
| void OnVisibilityChanged(content::Visibility visibility) override { |
| switch (visibility) { |
| case content::Visibility::VISIBLE: { |
| #if defined(USE_AURA) |
| valid_root_while_shown_ = |
| web_contents()->GetNativeView()->GetRootWindow() != NULL; |
| #endif |
| was_shown_ = true; |
| ++shown_count_; |
| break; |
| } |
| case content::Visibility::HIDDEN: { |
| was_shown_ = false; |
| ++hidden_count_; |
| break; |
| } |
| default: { |
| ADD_FAILURE() << "Unexpected call to OnVisibilityChanged."; |
| break; |
| } |
| } |
| } |
| |
| bool was_shown() const { return was_shown_; } |
| |
| int shown_count() const { return shown_count_; } |
| |
| int hidden_count() const { return hidden_count_; } |
| |
| bool valid_root_while_shown() const { return valid_root_while_shown_; } |
| |
| private: |
| content::WebContents* web_contents_; |
| bool was_shown_; |
| int32_t shown_count_; |
| int32_t hidden_count_; |
| // Set to true if the view containing the webcontents has a valid root window. |
| bool valid_root_while_shown_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebViewTestWebContentsObserver); |
| }; |
| |
| // Fakes the fullscreen browser state reported to WebContents and WebView. |
| class WebViewTestWebContentsDelegate : public content::WebContentsDelegate { |
| public: |
| WebViewTestWebContentsDelegate() : is_fullscreened_(false) {} |
| ~WebViewTestWebContentsDelegate() override {} |
| |
| void set_is_fullscreened(bool fs) { is_fullscreened_ = fs; } |
| |
| // content::WebContentsDelegate overrides. |
| bool IsFullscreenForTabOrPending( |
| const content::WebContents* ignored) const override { |
| return is_fullscreened_; |
| } |
| |
| private: |
| bool is_fullscreened_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebViewTestWebContentsDelegate); |
| }; |
| |
| } // namespace |
| |
| // Provides functionality to test a WebView. |
| class WebViewUnitTest : public views::test::WidgetTest { |
| public: |
| WebViewUnitTest() |
| : views::test::WidgetTest( |
| std::make_unique<content::TestBrowserThreadBundle>()) {} |
| |
| ~WebViewUnitTest() override {} |
| |
| std::unique_ptr<content::WebContents> CreateWebContentsForWebView( |
| content::BrowserContext* browser_context) { |
| return content::WebContentsTester::CreateTestWebContents(browser_context, |
| nullptr); |
| } |
| |
| void SetUp() override { |
| views::WebView::WebContentsCreator creator = base::BindRepeating( |
| &WebViewUnitTest::CreateWebContentsForWebView, base::Unretained(this)); |
| scoped_web_contents_creator_ = |
| std::make_unique<views::WebView::ScopedWebContentsCreatorForTesting>( |
| creator); |
| set_views_delegate(base::WrapUnique(new views::TestViewsDelegate)); |
| browser_context_.reset(new content::TestBrowserContext); |
| WidgetTest::SetUp(); |
| // Set the test content browser client to avoid pulling in needless |
| // dependencies from content. |
| SetBrowserClientForTesting(&test_browser_client_); |
| |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kDisableBackgroundingOccludedWindowsForTesting); |
| |
| // Create a top level widget and add a child, and give it a WebView as a |
| // child. |
| top_level_widget_ = CreateTopLevelFramelessPlatformWidget(); |
| top_level_widget_->SetBounds(gfx::Rect(0, 10, 100, 100)); |
| View* const contents_view = new View(); |
| top_level_widget_->SetContentsView(contents_view); |
| web_view_ = new WebView(browser_context_.get()); |
| web_view_->SetBoundsRect(gfx::Rect(contents_view->size())); |
| contents_view->AddChildView(web_view_); |
| top_level_widget_->Show(); |
| ASSERT_EQ(gfx::Rect(0, 0, 100, 100), web_view_->bounds()); |
| } |
| |
| void TearDown() override { |
| scoped_web_contents_creator_.reset(); |
| top_level_widget_->Close(); // Deletes all children and itself. |
| RunPendingMessages(); |
| |
| browser_context_.reset(NULL); |
| // Flush the message loop to execute pending relase tasks as this would |
| // upset ASAN and Valgrind. |
| RunPendingMessages(); |
| WidgetTest::TearDown(); |
| } |
| |
| protected: |
| Widget* top_level_widget() const { return top_level_widget_; } |
| WebView* web_view() const { return web_view_; } |
| NativeViewHost* holder() const { return web_view_->holder_; } |
| |
| std::unique_ptr<content::WebContents> CreateWebContents() const { |
| return content::WebContents::Create( |
| content::WebContents::CreateParams(browser_context_.get())); |
| } |
| |
| private: |
| content::RenderViewHostTestEnabler rvh_enabler_; |
| std::unique_ptr<content::TestBrowserContext> browser_context_; |
| content::TestContentBrowserClient test_browser_client_; |
| std::unique_ptr<views::WebView::ScopedWebContentsCreatorForTesting> |
| scoped_web_contents_creator_; |
| |
| Widget* top_level_widget_ = nullptr; |
| WebView* web_view_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebViewUnitTest); |
| }; |
| |
| // Tests that attaching and detaching a WebContents to a WebView makes the |
| // WebContents visible and hidden respectively. |
| TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) { |
| // Case 1: Create a new WebContents and set it in the webview via |
| // SetWebContents. This should make the WebContents visible. |
| const std::unique_ptr<content::WebContents> web_contents1( |
| CreateWebContents()); |
| WebViewTestWebContentsObserver observer1(web_contents1.get()); |
| EXPECT_FALSE(observer1.was_shown()); |
| |
| web_view()->SetWebContents(web_contents1.get()); |
| EXPECT_TRUE(observer1.was_shown()); |
| #if defined(USE_AURA) |
| EXPECT_TRUE(web_contents1->GetNativeView()->IsVisible()); |
| #endif |
| EXPECT_EQ(observer1.shown_count(), 1); |
| EXPECT_EQ(observer1.hidden_count(), 0); |
| EXPECT_TRUE(observer1.valid_root_while_shown()); |
| |
| // Case 2: Create another WebContents and replace the current WebContents |
| // via SetWebContents(). This should hide the current WebContents and show |
| // the new one. |
| const std::unique_ptr<content::WebContents> web_contents2( |
| CreateWebContents()); |
| WebViewTestWebContentsObserver observer2(web_contents2.get()); |
| EXPECT_FALSE(observer2.was_shown()); |
| |
| // Setting the new WebContents should hide the existing one. |
| web_view()->SetWebContents(web_contents2.get()); |
| EXPECT_FALSE(observer1.was_shown()); |
| EXPECT_TRUE(observer2.was_shown()); |
| EXPECT_TRUE(observer2.valid_root_while_shown()); |
| |
| // WebContents1 should not get stray show calls when WebContents2 is set. |
| EXPECT_EQ(observer1.shown_count(), 1); |
| EXPECT_EQ(observer1.hidden_count(), 1); |
| EXPECT_EQ(observer2.shown_count(), 1); |
| EXPECT_EQ(observer2.hidden_count(), 0); |
| |
| // Case 3: Test that attaching to a hidden webview does not show the web |
| // contents. |
| web_view()->SetVisible(false); |
| EXPECT_EQ(1, observer2.hidden_count()); // Now hidden. |
| |
| EXPECT_EQ(1, observer1.shown_count()); |
| web_view()->SetWebContents(web_contents1.get()); |
| EXPECT_EQ(1, observer1.shown_count()); |
| |
| // Nothing else should change. |
| EXPECT_EQ(1, observer1.hidden_count()); |
| EXPECT_EQ(1, observer2.shown_count()); |
| EXPECT_EQ(1, observer2.hidden_count()); |
| |
| #if defined(USE_AURA) |
| // Case 4: Test that making the webview visible when a window has an invisible |
| // parent does not make the web contents visible. |
| top_level_widget()->Hide(); |
| web_view()->SetVisible(true); |
| EXPECT_EQ(1, observer1.shown_count()); |
| top_level_widget()->Show(); |
| EXPECT_EQ(2, observer1.shown_count()); |
| top_level_widget()->Hide(); |
| EXPECT_EQ(2, observer1.hidden_count()); |
| #else |
| // On Mac, changes to window visibility do not trigger calls to WebContents:: |
| // WasShown() or WasHidden(), since the OS does not provide good signals for |
| // window visibility. However, we can still test that moving a visible WebView |
| // whose WebContents is not currently showing to a new, visible window will |
| // show the WebContents. Simulate the "hide window with visible WebView" step |
| // simply by detaching the WebContents. |
| web_view()->SetVisible(true); |
| EXPECT_EQ(2, observer1.shown_count()); |
| web_view()->holder()->Detach(); |
| EXPECT_EQ(2, observer1.hidden_count()); |
| #endif |
| // Case 5: Test that moving from a hidden parent to a visible parent makes the |
| // web contents visible. |
| Widget* parent2 = CreateTopLevelFramelessPlatformWidget(); |
| parent2->SetBounds(gfx::Rect(0, 10, 100, 100)); |
| parent2->Show(); |
| EXPECT_EQ(2, observer1.shown_count()); |
| // Note: that reparenting the windows directly, after the windows have been |
| // created, e.g., Widget::ReparentNativeView(widget, parent2), is not a |
| // supported use case. Instead, move the WebView over. |
| parent2->SetContentsView(web_view()); |
| EXPECT_EQ(3, observer1.shown_count()); |
| parent2->Close(); |
| } |
| |
| // Tests that the layout of the NativeViewHost within WebView behaves as |
| // expected when embedding a fullscreen widget during WebContents screen |
| // capture. |
| TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Layout) { |
| web_view()->SetEmbedFullscreenWidgetMode(true); |
| ASSERT_EQ(1, web_view()->child_count()); |
| |
| const std::unique_ptr<content::WebContents> web_contents(CreateWebContents()); |
| WebViewTestWebContentsDelegate delegate; |
| web_contents->SetDelegate(&delegate); |
| web_view()->SetWebContents(web_contents.get()); |
| |
| // Initially, the holder should fill the entire WebView. |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); |
| |
| // Simulate a transition into fullscreen mode, but without screen capture |
| // active on the WebContents, the holder should still fill the entire |
| // WebView like before. |
| delegate.set_is_fullscreened(true); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(true, false); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); |
| |
| // ...and transition back out of fullscreen mode. |
| delegate.set_is_fullscreened(false); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(false, false); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); |
| |
| // Now, begin screen capture of the WebContents and then enter fullscreen |
| // mode. This time, the holder should be centered within WebView and |
| // sized to match the capture size. |
| const gfx::Size capture_size(64, 48); |
| web_contents->IncrementCapturerCount(capture_size); |
| delegate.set_is_fullscreened(true); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(true, false); |
| |
| // The expected size should be scaled to whichever dimension matches the |
| // holder first, with the other scaled from the capture size to match the |
| // holder. So 100, 100 holder size and 64, 48 capture size gives: |
| // 100 / 64 * 48 = 75 |
| // The positioning centers the unmatched holder/capture dimension, giving: |
| // (100 - 75 = 25) / 2 = 12 |
| EXPECT_EQ(gfx::Rect(0, 12, 100, 75), holder()->bounds()); |
| |
| // Resize the WebView so that its width is smaller than the capture width. |
| // Expect the holder to be scaled-down, letterboxed style. |
| web_view()->SetBoundsRect(gfx::Rect(0, 0, 32, 100)); |
| EXPECT_EQ(gfx::Rect(0, 38, 32, 24), holder()->bounds()); |
| |
| // Resize the WebView so that its height is smaller than the capture height. |
| // Expect the holder to be scaled-down, pillarboxed style. |
| web_view()->SetBoundsRect(gfx::Rect(0, 0, 100, 24)); |
| EXPECT_EQ(gfx::Rect(34, 0, 32, 24), holder()->bounds()); |
| |
| // Transition back out of fullscreen mode a final time and confirm the bounds |
| // of the holder fill the entire WebView once again. |
| delegate.set_is_fullscreened(false); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(false, false); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 24), holder()->bounds()); |
| } |
| |
| // Tests that a WebView correctly switches between WebContentses when one of |
| // them is embedding a fullscreen widget during WebContents screen capture. |
| TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Switching) { |
| web_view()->SetEmbedFullscreenWidgetMode(true); |
| ASSERT_EQ(1, web_view()->child_count()); |
| const gfx::NativeView unset_native_view = holder()->native_view(); |
| |
| // Create two WebContentses to switch between. |
| const std::unique_ptr<content::WebContents> web_contents1( |
| CreateWebContents()); |
| WebViewTestWebContentsDelegate delegate1; |
| web_contents1->SetDelegate(&delegate1); |
| const std::unique_ptr<content::WebContents> web_contents2( |
| CreateWebContents()); |
| WebViewTestWebContentsDelegate delegate2; |
| web_contents2->SetDelegate(&delegate2); |
| |
| EXPECT_NE(web_contents1->GetNativeView(), holder()->native_view()); |
| web_view()->SetWebContents(web_contents1.get()); |
| EXPECT_EQ(web_contents1->GetNativeView(), holder()->native_view()); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); |
| |
| // Begin screen capture of the WebContents and then enter fullscreen mode. |
| // The native view should not have changed, but the layout of its holder will |
| // have (indicates WebView has responded). |
| const gfx::Size capture_size(64, 48); |
| web_contents1->IncrementCapturerCount(capture_size); |
| delegate1.set_is_fullscreened(true); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(true, false); |
| EXPECT_EQ(web_contents1->GetNativeView(), holder()->native_view()); |
| EXPECT_EQ(gfx::Rect(0, 12, 100, 75), holder()->bounds()); |
| |
| // When setting the WebContents to nullptr, the native view should become |
| // unset. |
| web_view()->SetWebContents(nullptr); |
| EXPECT_EQ(unset_native_view, holder()->native_view()); |
| |
| // ...and when setting the WebContents back to the currently-fullscreened |
| // instance, expect the native view and layout to reflect that. |
| web_view()->SetWebContents(web_contents1.get()); |
| EXPECT_EQ(web_contents1->GetNativeView(), holder()->native_view()); |
| EXPECT_EQ(gfx::Rect(0, 12, 100, 75), holder()->bounds()); |
| |
| // Now, switch to a different, non-null WebContents instance and check that |
| // the native view has changed and the holder is filling WebView again. |
| web_view()->SetWebContents(web_contents2.get()); |
| EXPECT_EQ(web_contents2->GetNativeView(), holder()->native_view()); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); |
| |
| // Finally, switch back to the first WebContents (still fullscreened). |
| web_view()->SetWebContents(web_contents1.get()); |
| EXPECT_EQ(web_contents1->GetNativeView(), holder()->native_view()); |
| EXPECT_EQ(gfx::Rect(0, 12, 100, 75), holder()->bounds()); |
| } |
| |
| // Tests that clicking anywhere within the bounds of WebView, and either outside |
| // or inside the bounds of its child NativeViewHost, causes WebView to gain |
| // focus. |
| TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_ClickToFocus) { |
| // For this test, add another View that can take focus away from WebView. |
| web_view()->SetBoundsRect(gfx::Rect(0, 0, 100, 90)); |
| views::View* const something_to_focus = new views::View(); |
| something_to_focus->SetBoundsRect(gfx::Rect(0, 90, 100, 10)); |
| something_to_focus->SetFocusBehavior(View::FocusBehavior::ALWAYS); |
| top_level_widget()->GetContentsView()->AddChildView(something_to_focus); |
| |
| web_view()->SetEmbedFullscreenWidgetMode(true); |
| ASSERT_EQ(1, web_view()->child_count()); |
| |
| const std::unique_ptr<content::WebContents> web_contents(CreateWebContents()); |
| WebViewTestWebContentsDelegate delegate; |
| web_contents->SetDelegate(&delegate); |
| web_view()->SetWebContents(web_contents.get()); |
| |
| // Begin screen capture of the WebContents and then enter fullscreen mode. |
| // The holder should be centered within WebView and sized to match the capture |
| // size. |
| const gfx::Size capture_size(64, 48); |
| web_contents->IncrementCapturerCount(capture_size); |
| delegate.set_is_fullscreened(true); |
| static_cast<content::WebContentsObserver*>(web_view())-> |
| DidToggleFullscreenModeForTab(true, false); |
| EXPECT_EQ(gfx::Rect(0, 7, 100, 75), holder()->bounds()); |
| |
| // Focus the other widget. |
| something_to_focus->RequestFocus(); |
| EXPECT_FALSE(web_view()->HasFocus()); |
| EXPECT_FALSE(holder()->HasFocus()); |
| EXPECT_TRUE(something_to_focus->HasFocus()); |
| |
| // Send mouse press event to WebView outside the bounds of the holder, and |
| // confirm WebView took focus. |
| const ui::MouseEvent click_outside_holder( |
| ui::ET_MOUSE_PRESSED, gfx::Point(1, 1), |
| gfx::Point(), // Immaterial. |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); |
| EXPECT_TRUE(static_cast<views::View*>(web_view())-> |
| OnMousePressed(click_outside_holder)); |
| EXPECT_TRUE(web_view()->HasFocus()); |
| EXPECT_FALSE(holder()->HasFocus()); |
| EXPECT_FALSE(something_to_focus->HasFocus()); |
| |
| // Focus the other widget again. |
| something_to_focus->RequestFocus(); |
| EXPECT_FALSE(web_view()->HasFocus()); |
| EXPECT_FALSE(holder()->HasFocus()); |
| EXPECT_TRUE(something_to_focus->HasFocus()); |
| |
| // Send a mouse press event within the bounds of the holder and expect no |
| // focus change. The reason is that WebView is not supposed to handle mouse |
| // events within the bounds of the holder, and it would be up to the |
| // WebContents native view to grab the focus instead. In this test |
| // environment, the WebContents native view doesn't include the implementation |
| // needed to grab focus, so no focus change will occur. |
| const ui::MouseEvent click_inside_holder( |
| ui::ET_MOUSE_PRESSED, web_view()->bounds().CenterPoint(), |
| gfx::Point(), // Immaterial. |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); |
| EXPECT_FALSE(static_cast<views::View*>(web_view())-> |
| OnMousePressed(click_inside_holder)); |
| EXPECT_FALSE(web_view()->HasFocus()); |
| EXPECT_FALSE(holder()->HasFocus()); |
| EXPECT_TRUE(something_to_focus->HasFocus()); |
| } |
| |
| // Verifies that there is no crash in WebView destructor |
| // if WebView is already removed from Widget. |
| TEST_F(WebViewUnitTest, DetachedWebViewDestructor) { |
| // Init WebView with attached NativeView. |
| const std::unique_ptr<content::WebContents> web_contents(CreateWebContents()); |
| std::unique_ptr<WebView> webview( |
| new WebView(web_contents->GetBrowserContext())); |
| View* contents_view = top_level_widget()->GetContentsView(); |
| contents_view->AddChildView(webview.get()); |
| webview->SetWebContents(web_contents.get()); |
| |
| // Remove WebView from views hierarchy. NativeView should be detached |
| // from Widget. |
| contents_view->RemoveChildView(webview.get()); |
| // Destroy WebView. NativeView should be detached secondary. |
| // There should be no crash. |
| webview.reset(); |
| } |
| |
| // Test that the specified crashed overlay view is shown when a WebContents |
| // is in a crashed state. |
| TEST_F(WebViewUnitTest, CrashedOverlayView) { |
| const std::unique_ptr<content::WebContents> web_contents(CreateWebContents()); |
| std::unique_ptr<WebView> web_view( |
| new WebView(web_contents->GetBrowserContext())); |
| View* contents_view = top_level_widget()->GetContentsView(); |
| contents_view->AddChildView(web_view.get()); |
| web_view->SetWebContents(web_contents.get()); |
| |
| View* crashed_overlay_view = new View(); |
| web_view->SetCrashedOverlayView(crashed_overlay_view); |
| EXPECT_FALSE(crashed_overlay_view->IsDrawn()); |
| |
| // Normally when a renderer crashes, the WebView will learn about it |
| // automatically via WebContentsObserver. Since this is a test |
| // WebContents, simulate that by calling SetIsCrashed and then |
| // explicitly calling RenderViewDeleted on the WebView to trigger it |
| // to swap in the crashed overlay view. |
| web_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| EXPECT_TRUE(web_contents->IsCrashed()); |
| static_cast<content::WebContentsObserver*>(web_view.get()) |
| ->RenderViewDeleted(nullptr); |
| EXPECT_TRUE(crashed_overlay_view->IsDrawn()); |
| } |
| |
| // Test that a crashed overlay view isn't deleted if it's owned by client. |
| TEST_F(WebViewUnitTest, CrashedOverlayViewOwnedbyClient) { |
| const std::unique_ptr<content::WebContents> web_contents(CreateWebContents()); |
| std::unique_ptr<WebView> web_view( |
| new WebView(web_contents->GetBrowserContext())); |
| View* contents_view = top_level_widget()->GetContentsView(); |
| contents_view->AddChildView(web_view.get()); |
| web_view->SetWebContents(web_contents.get()); |
| |
| View* crashed_overlay_view = new View(); |
| crashed_overlay_view->set_owned_by_client(); |
| web_view->SetCrashedOverlayView(crashed_overlay_view); |
| EXPECT_FALSE(crashed_overlay_view->IsDrawn()); |
| |
| // Simulate a renderer crash (see above). |
| web_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| EXPECT_TRUE(web_contents->IsCrashed()); |
| static_cast<content::WebContentsObserver*>(web_view.get()) |
| ->RenderViewDeleted(nullptr); |
| EXPECT_TRUE(crashed_overlay_view->IsDrawn()); |
| |
| web_view->SetCrashedOverlayView(nullptr); |
| web_view.reset(); |
| |
| // This shouldn't crash, we still own this. |
| delete crashed_overlay_view; |
| } |
| |
| } // namespace views |