| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/render_widget_host_view_aura.h" |
| |
| #include "base/basictypes.h" |
| #include "base/memory/shared_memory.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/output/compositor_frame_metadata.h" |
| #include "cc/output/gl_frame_data.h" |
| #include "content/browser/aura/resize_lock.h" |
| #include "content/browser/browser_thread_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_delegate.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/common/gpu/gpu_messages.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/test/mock_render_process_host.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "ipc/ipc_test_sink.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/screen_position_client.h" |
| #include "ui/aura/client/window_tree_client.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/layout_manager.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/test/aura_test_helper.h" |
| #include "ui/aura/test/test_cursor_client.h" |
| #include "ui/aura/test/test_screen.h" |
| #include "ui/aura/test/test_window_delegate.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_observer.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/compositor/test/test_context_factory.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| |
| using testing::_; |
| |
| namespace content { |
| namespace { |
| |
| // Simple screen position client to test coordinate system conversion. |
| class TestScreenPositionClient |
| : public aura::client::ScreenPositionClient { |
| public: |
| TestScreenPositionClient() {} |
| virtual ~TestScreenPositionClient() {} |
| |
| // aura::client::ScreenPositionClient overrides: |
| virtual void ConvertPointToScreen(const aura::Window* window, |
| gfx::Point* point) OVERRIDE { |
| point->Offset(-1, -1); |
| } |
| |
| virtual void ConvertPointFromScreen(const aura::Window* window, |
| gfx::Point* point) OVERRIDE { |
| point->Offset(1, 1); |
| } |
| |
| virtual void ConvertHostPointToScreen(aura::Window* window, |
| gfx::Point* point) OVERRIDE { |
| ConvertPointToScreen(window, point); |
| } |
| |
| virtual void SetBounds(aura::Window* window, |
| const gfx::Rect& bounds, |
| const gfx::Display& display) OVERRIDE { |
| } |
| }; |
| |
| class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate { |
| public: |
| MockRenderWidgetHostDelegate() {} |
| virtual ~MockRenderWidgetHostDelegate() {} |
| }; |
| |
| // Simple observer that keeps track of changes to a window for tests. |
| class TestWindowObserver : public aura::WindowObserver { |
| public: |
| explicit TestWindowObserver(aura::Window* window_to_observe) |
| : window_(window_to_observe) { |
| window_->AddObserver(this); |
| } |
| virtual ~TestWindowObserver() { |
| if (window_) |
| window_->RemoveObserver(this); |
| } |
| |
| bool destroyed() const { return destroyed_; } |
| |
| // aura::WindowObserver overrides: |
| virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { |
| CHECK_EQ(window, window_); |
| destroyed_ = true; |
| window_ = NULL; |
| } |
| |
| private: |
| // Window that we're observing, or NULL if it's been destroyed. |
| aura::Window* window_; |
| |
| // Was |window_| destroyed? |
| bool destroyed_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWindowObserver); |
| }; |
| |
| class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura { |
| public: |
| FakeRenderWidgetHostViewAura(RenderWidgetHost* widget) |
| : RenderWidgetHostViewAura(widget), has_resize_lock_(false) {} |
| |
| virtual ~FakeRenderWidgetHostViewAura() {} |
| |
| virtual bool ShouldCreateResizeLock() OVERRIDE { |
| gfx::Size desired_size = window()->bounds().size(); |
| return desired_size != current_frame_size(); |
| } |
| |
| virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock) |
| OVERRIDE { |
| gfx::Size desired_size = window()->bounds().size(); |
| return scoped_ptr<ResizeLock>( |
| new FakeResizeLock(desired_size, defer_compositor_lock)); |
| } |
| |
| void RunOnCompositingDidCommit() { |
| OnCompositingDidCommit(window()->GetDispatcher()->compositor()); |
| } |
| |
| // A lock that doesn't actually do anything to the compositor, and does not |
| // time out. |
| class FakeResizeLock : public ResizeLock { |
| public: |
| FakeResizeLock(const gfx::Size new_size, bool defer_compositor_lock) |
| : ResizeLock(new_size, defer_compositor_lock) {} |
| }; |
| |
| bool has_resize_lock_; |
| gfx::Size last_frame_size_; |
| }; |
| |
| class RenderWidgetHostViewAuraTest : public testing::Test { |
| public: |
| RenderWidgetHostViewAuraTest() |
| : browser_thread_for_ui_(BrowserThread::UI, &message_loop_) {} |
| |
| virtual void SetUp() { |
| ImageTransportFactory::InitializeForUnitTests( |
| scoped_ptr<ui::ContextFactory>(new ui::TestContextFactory)); |
| aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); |
| aura_test_helper_->SetUp(); |
| |
| browser_context_.reset(new TestBrowserContext); |
| process_host_ = new MockRenderProcessHost(browser_context_.get()); |
| |
| sink_ = &process_host_->sink(); |
| |
| parent_host_ = new RenderWidgetHostImpl( |
| &delegate_, process_host_, MSG_ROUTING_NONE, false); |
| parent_view_ = static_cast<RenderWidgetHostViewAura*>( |
| RenderWidgetHostView::CreateViewForWidget(parent_host_)); |
| parent_view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext(parent_view_->GetNativeView(), |
| aura_test_helper_->root_window(), |
| gfx::Rect()); |
| |
| widget_host_ = new RenderWidgetHostImpl( |
| &delegate_, process_host_, MSG_ROUTING_NONE, false); |
| widget_host_->Init(); |
| widget_host_->OnMessageReceived( |
| ViewHostMsg_DidActivateAcceleratedCompositing(0, true)); |
| view_ = new FakeRenderWidgetHostViewAura(widget_host_); |
| } |
| |
| virtual void TearDown() { |
| sink_ = NULL; |
| process_host_ = NULL; |
| if (view_) |
| view_->Destroy(); |
| delete widget_host_; |
| |
| parent_view_->Destroy(); |
| delete parent_host_; |
| |
| browser_context_.reset(); |
| aura_test_helper_->TearDown(); |
| |
| message_loop_.DeleteSoon(FROM_HERE, browser_context_.release()); |
| message_loop_.RunUntilIdle(); |
| ImageTransportFactory::Terminate(); |
| } |
| |
| protected: |
| base::MessageLoopForUI message_loop_; |
| BrowserThreadImpl browser_thread_for_ui_; |
| scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_; |
| scoped_ptr<BrowserContext> browser_context_; |
| MockRenderWidgetHostDelegate delegate_; |
| MockRenderProcessHost* process_host_; |
| |
| // Tests should set these to NULL if they've already triggered their |
| // destruction. |
| RenderWidgetHostImpl* parent_host_; |
| RenderWidgetHostViewAura* parent_view_; |
| |
| // Tests should set these to NULL if they've already triggered their |
| // destruction. |
| RenderWidgetHostImpl* widget_host_; |
| FakeRenderWidgetHostViewAura* view_; |
| |
| IPC::TestSink* sink_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraTest); |
| }; |
| |
| // A layout manager that always resizes a child to the root window size. |
| class FullscreenLayoutManager : public aura::LayoutManager { |
| public: |
| explicit FullscreenLayoutManager(aura::RootWindow* owner) |
| : owner_(owner) {} |
| virtual ~FullscreenLayoutManager() {} |
| |
| // Overridden from aura::LayoutManager: |
| virtual void OnWindowResized() OVERRIDE { |
| aura::Window::Windows::const_iterator i; |
| for (i = owner_->children().begin(); i != owner_->children().end(); ++i) { |
| (*i)->SetBounds(gfx::Rect()); |
| } |
| } |
| virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { |
| child->SetBounds(gfx::Rect()); |
| } |
| virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE { |
| } |
| virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE { |
| } |
| virtual void OnChildWindowVisibilityChanged(aura::Window* child, |
| bool visible) OVERRIDE { |
| } |
| virtual void SetChildBounds(aura::Window* child, |
| const gfx::Rect& requested_bounds) OVERRIDE { |
| SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size())); |
| } |
| |
| private: |
| aura::RootWindow* owner_; |
| DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager); |
| }; |
| |
| class MockWindowObserver : public aura::WindowObserver { |
| public: |
| MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&)); |
| }; |
| |
| } // namespace |
| |
| // Checks that a fullscreen view has the correct show-state and receives the |
| // focus. |
| TEST_F(RenderWidgetHostViewAuraTest, FocusFullscreen) { |
| view_->InitAsFullscreen(parent_view_); |
| aura::Window* window = view_->GetNativeView(); |
| ASSERT_TRUE(window != NULL); |
| EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, |
| window->GetProperty(aura::client::kShowStateKey)); |
| |
| // Check that we requested and received the focus. |
| EXPECT_TRUE(window->HasFocus()); |
| |
| // Check that we'll also say it's okay to activate the window when there's an |
| // ActivationClient defined. |
| EXPECT_TRUE(view_->ShouldActivate()); |
| } |
| |
| // Checks that a popup is positioned correctly relative to its parent using |
| // screen coordinates. |
| TEST_F(RenderWidgetHostViewAuraTest, PositionChildPopup) { |
| TestScreenPositionClient screen_position_client; |
| |
| aura::Window* window = parent_view_->GetNativeView(); |
| aura::Window* root = window->GetRootWindow(); |
| aura::client::SetScreenPositionClient(root, &screen_position_client); |
| |
| parent_view_->SetBounds(gfx::Rect(10, 10, 800, 600)); |
| gfx::Rect bounds_in_screen = parent_view_->GetViewBounds(); |
| int horiz = bounds_in_screen.width() / 4; |
| int vert = bounds_in_screen.height() / 4; |
| bounds_in_screen.Inset(horiz, vert); |
| |
| // Verify that when the popup is initialized for the first time, it correctly |
| // treats the input bounds as screen coordinates. |
| view_->InitAsPopup(parent_view_, bounds_in_screen); |
| |
| gfx::Rect final_bounds_in_screen = view_->GetViewBounds(); |
| EXPECT_EQ(final_bounds_in_screen.ToString(), bounds_in_screen.ToString()); |
| |
| // Verify that directly setting the bounds via SetBounds() treats the input |
| // as screen coordinates. |
| bounds_in_screen = gfx::Rect(60, 60, 100, 100); |
| view_->SetBounds(bounds_in_screen); |
| final_bounds_in_screen = view_->GetViewBounds(); |
| EXPECT_EQ(final_bounds_in_screen.ToString(), bounds_in_screen.ToString()); |
| |
| // Verify that setting the size does not alter the origin. |
| gfx::Point original_origin = window->bounds().origin(); |
| view_->SetSize(gfx::Size(120, 120)); |
| gfx::Point new_origin = window->bounds().origin(); |
| EXPECT_EQ(original_origin.ToString(), new_origin.ToString()); |
| |
| aura::client::SetScreenPositionClient(root, NULL); |
| } |
| |
| // Checks that a fullscreen view is destroyed when it loses the focus. |
| TEST_F(RenderWidgetHostViewAuraTest, DestroyFullscreenOnBlur) { |
| view_->InitAsFullscreen(parent_view_); |
| aura::Window* window = view_->GetNativeView(); |
| ASSERT_TRUE(window != NULL); |
| ASSERT_TRUE(window->HasFocus()); |
| |
| // After we create and focus another window, the RWHVA's window should be |
| // destroyed. |
| TestWindowObserver observer(window); |
| aura::test::TestWindowDelegate delegate; |
| scoped_ptr<aura::Window> sibling(new aura::Window(&delegate)); |
| sibling->Init(ui::LAYER_TEXTURED); |
| sibling->Show(); |
| window->parent()->AddChild(sibling.get()); |
| sibling->Focus(); |
| ASSERT_TRUE(sibling->HasFocus()); |
| ASSERT_TRUE(observer.destroyed()); |
| |
| widget_host_ = NULL; |
| view_ = NULL; |
| } |
| |
| // Checks that IME-composition-event state is maintained correctly. |
| TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) { |
| view_->InitAsChild(NULL); |
| view_->Show(); |
| |
| ui::CompositionText composition_text; |
| composition_text.text = ASCIIToUTF16("|a|b"); |
| |
| // Focused segment |
| composition_text.underlines.push_back( |
| ui::CompositionUnderline(0, 3, 0xff000000, true)); |
| |
| // Non-focused segment |
| composition_text.underlines.push_back( |
| ui::CompositionUnderline(3, 4, 0xff000000, false)); |
| |
| const ui::CompositionUnderlines& underlines = composition_text.underlines; |
| |
| // Caret is at the end. (This emulates Japanese MSIME 2007 and later) |
| composition_text.selection = gfx::Range(4); |
| |
| sink_->ClearMessages(); |
| view_->SetCompositionText(composition_text); |
| EXPECT_TRUE(view_->has_composition_text_); |
| { |
| const IPC::Message* msg = |
| sink_->GetFirstMessageMatching(ViewMsg_ImeSetComposition::ID); |
| ASSERT_TRUE(msg != NULL); |
| |
| ViewMsg_ImeSetComposition::Param params; |
| ViewMsg_ImeSetComposition::Read(msg, ¶ms); |
| // composition text |
| EXPECT_EQ(composition_text.text, params.a); |
| // underlines |
| ASSERT_EQ(underlines.size(), params.b.size()); |
| for (size_t i = 0; i < underlines.size(); ++i) { |
| EXPECT_EQ(underlines[i].start_offset, params.b[i].startOffset); |
| EXPECT_EQ(underlines[i].end_offset, params.b[i].endOffset); |
| EXPECT_EQ(underlines[i].color, params.b[i].color); |
| EXPECT_EQ(underlines[i].thick, params.b[i].thick); |
| } |
| // highlighted range |
| EXPECT_EQ(4, params.c) << "Should be the same to the caret pos"; |
| EXPECT_EQ(4, params.d) << "Should be the same to the caret pos"; |
| } |
| |
| view_->ImeCancelComposition(); |
| EXPECT_FALSE(view_->has_composition_text_); |
| } |
| |
| // Checks that touch-event state is maintained correctly. |
| TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) { |
| view_->InitAsChild(NULL); |
| view_->Show(); |
| |
| // Start with no touch-event handler in the renderer. |
| widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false)); |
| EXPECT_FALSE(widget_host_->ShouldForwardTouchEvent()); |
| |
| ui::TouchEvent press(ui::ET_TOUCH_PRESSED, |
| gfx::Point(30, 30), |
| 0, |
| ui::EventTimeForNow()); |
| ui::TouchEvent move(ui::ET_TOUCH_MOVED, |
| gfx::Point(20, 20), |
| 0, |
| ui::EventTimeForNow()); |
| ui::TouchEvent release(ui::ET_TOUCH_RELEASED, |
| gfx::Point(20, 20), |
| 0, |
| ui::EventTimeForNow()); |
| |
| view_->OnTouchEvent(&press); |
| EXPECT_FALSE(press.handled()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchStart, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StatePressed, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&move); |
| EXPECT_FALSE(move.handled()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchMove, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StateMoved, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&release); |
| EXPECT_FALSE(release.handled()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchEnd, view_->touch_event_.type); |
| EXPECT_EQ(0U, view_->touch_event_.touchesLength); |
| |
| // Now install some touch-event handlers and do the same steps. The touch |
| // events should now be consumed. However, the touch-event state should be |
| // updated as before. |
| widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| EXPECT_TRUE(widget_host_->ShouldForwardTouchEvent()); |
| |
| view_->OnTouchEvent(&press); |
| EXPECT_TRUE(press.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchStart, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StatePressed, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&move); |
| EXPECT_TRUE(move.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchMove, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StateMoved, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&release); |
| EXPECT_TRUE(release.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchEnd, view_->touch_event_.type); |
| EXPECT_EQ(0U, view_->touch_event_.touchesLength); |
| |
| // Now start a touch event, and remove the event-handlers before the release. |
| view_->OnTouchEvent(&press); |
| EXPECT_TRUE(press.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchStart, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StatePressed, |
| view_->touch_event_.touches[0].state); |
| |
| widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false)); |
| EXPECT_FALSE(widget_host_->ShouldForwardTouchEvent()); |
| |
| ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(20, 20), 0, |
| base::Time::NowFromSystemTime() - base::Time()); |
| view_->OnTouchEvent(&move2); |
| EXPECT_FALSE(move2.handled()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchMove, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StateMoved, |
| view_->touch_event_.touches[0].state); |
| |
| ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(20, 20), 0, |
| base::Time::NowFromSystemTime() - base::Time()); |
| view_->OnTouchEvent(&release2); |
| EXPECT_FALSE(release2.handled()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchEnd, view_->touch_event_.type); |
| EXPECT_EQ(0U, view_->touch_event_.touchesLength); |
| } |
| |
| // Checks that touch-events are queued properly when there is a touch-event |
| // handler on the page. |
| TEST_F(RenderWidgetHostViewAuraTest, TouchEventSyncAsync) { |
| view_->InitAsChild(NULL); |
| view_->Show(); |
| |
| widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| EXPECT_TRUE(widget_host_->ShouldForwardTouchEvent()); |
| |
| ui::TouchEvent press(ui::ET_TOUCH_PRESSED, |
| gfx::Point(30, 30), |
| 0, |
| ui::EventTimeForNow()); |
| ui::TouchEvent move(ui::ET_TOUCH_MOVED, |
| gfx::Point(20, 20), |
| 0, |
| ui::EventTimeForNow()); |
| ui::TouchEvent release(ui::ET_TOUCH_RELEASED, |
| gfx::Point(20, 20), |
| 0, |
| ui::EventTimeForNow()); |
| |
| view_->OnTouchEvent(&press); |
| EXPECT_TRUE(press.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchStart, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StatePressed, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&move); |
| EXPECT_TRUE(move.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchMove, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StateMoved, |
| view_->touch_event_.touches[0].state); |
| |
| // Send the same move event. Since the point hasn't moved, it won't affect the |
| // queue. However, the view should consume the event. |
| view_->OnTouchEvent(&move); |
| EXPECT_TRUE(move.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchMove, view_->touch_event_.type); |
| EXPECT_EQ(1U, view_->touch_event_.touchesLength); |
| EXPECT_EQ(WebKit::WebTouchPoint::StateMoved, |
| view_->touch_event_.touches[0].state); |
| |
| view_->OnTouchEvent(&release); |
| EXPECT_TRUE(release.stopped_propagation()); |
| EXPECT_EQ(WebKit::WebInputEvent::TouchEnd, view_->touch_event_.type); |
| EXPECT_EQ(0U, view_->touch_event_.touchesLength); |
| } |
| |
| TEST_F(RenderWidgetHostViewAuraTest, PhysicalBackingSizeWithScale) { |
| view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| view_->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| sink_->ClearMessages(); |
| view_->SetSize(gfx::Size(100, 100)); |
| EXPECT_EQ("100x100", view_->GetPhysicalBackingSize().ToString()); |
| EXPECT_EQ(1u, sink_->message_count()); |
| EXPECT_EQ(ViewMsg_Resize::ID, sink_->GetMessageAt(0)->type()); |
| { |
| const IPC::Message* msg = sink_->GetMessageAt(0); |
| EXPECT_EQ(ViewMsg_Resize::ID, msg->type()); |
| ViewMsg_Resize::Param params; |
| ViewMsg_Resize::Read(msg, ¶ms); |
| EXPECT_EQ("100x100", params.a.new_size.ToString()); // dip size |
| EXPECT_EQ("100x100", |
| params.a.physical_backing_size.ToString()); // backing size |
| } |
| |
| widget_host_->ResetSizeAndRepaintPendingFlags(); |
| sink_->ClearMessages(); |
| |
| aura_test_helper_->test_screen()->SetDeviceScaleFactor(2.0f); |
| EXPECT_EQ("200x200", view_->GetPhysicalBackingSize().ToString()); |
| // Extra ScreenInfoChanged message for |parent_view_|. |
| EXPECT_EQ(1u, sink_->message_count()); |
| { |
| const IPC::Message* msg = sink_->GetMessageAt(0); |
| EXPECT_EQ(ViewMsg_Resize::ID, msg->type()); |
| ViewMsg_Resize::Param params; |
| ViewMsg_Resize::Read(msg, ¶ms); |
| EXPECT_EQ(2.0f, params.a.screen_info.deviceScaleFactor); |
| EXPECT_EQ("100x100", params.a.new_size.ToString()); // dip size |
| EXPECT_EQ("200x200", |
| params.a.physical_backing_size.ToString()); // backing size |
| } |
| |
| widget_host_->ResetSizeAndRepaintPendingFlags(); |
| sink_->ClearMessages(); |
| |
| aura_test_helper_->test_screen()->SetDeviceScaleFactor(1.0f); |
| // Extra ScreenInfoChanged message for |parent_view_|. |
| EXPECT_EQ(1u, sink_->message_count()); |
| EXPECT_EQ("100x100", view_->GetPhysicalBackingSize().ToString()); |
| { |
| const IPC::Message* msg = sink_->GetMessageAt(0); |
| EXPECT_EQ(ViewMsg_Resize::ID, msg->type()); |
| ViewMsg_Resize::Param params; |
| ViewMsg_Resize::Read(msg, ¶ms); |
| EXPECT_EQ(1.0f, params.a.screen_info.deviceScaleFactor); |
| EXPECT_EQ("100x100", params.a.new_size.ToString()); // dip size |
| EXPECT_EQ("100x100", |
| params.a.physical_backing_size.ToString()); // backing size |
| } |
| } |
| |
| // Checks that InputMsg_CursorVisibilityChange IPC messages are dispatched |
| // to the renderer at the correct times. |
| TEST_F(RenderWidgetHostViewAuraTest, CursorVisibilityChange) { |
| view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| view_->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| view_->SetSize(gfx::Size(100, 100)); |
| |
| aura::test::TestCursorClient cursor_client( |
| parent_view_->GetNativeView()->GetRootWindow()); |
| |
| cursor_client.AddObserver(view_); |
| |
| // Expect a message the first time the cursor is shown. |
| view_->WasShown(); |
| sink_->ClearMessages(); |
| cursor_client.ShowCursor(); |
| EXPECT_EQ(1u, sink_->message_count()); |
| EXPECT_TRUE(sink_->GetUniqueMessageMatching( |
| InputMsg_CursorVisibilityChange::ID)); |
| |
| // No message expected if the renderer already knows the cursor is visible. |
| sink_->ClearMessages(); |
| cursor_client.ShowCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| |
| // Hiding the cursor should send a message. |
| sink_->ClearMessages(); |
| cursor_client.HideCursor(); |
| EXPECT_EQ(1u, sink_->message_count()); |
| EXPECT_TRUE(sink_->GetUniqueMessageMatching( |
| InputMsg_CursorVisibilityChange::ID)); |
| |
| // No message expected if the renderer already knows the cursor is invisible. |
| sink_->ClearMessages(); |
| cursor_client.HideCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| |
| // No messages should be sent while the view is invisible. |
| view_->WasHidden(); |
| sink_->ClearMessages(); |
| cursor_client.ShowCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| cursor_client.HideCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| |
| // Show the view. Since the cursor was invisible when the view was hidden, |
| // no message should be sent. |
| sink_->ClearMessages(); |
| view_->WasShown(); |
| EXPECT_FALSE(sink_->GetUniqueMessageMatching( |
| InputMsg_CursorVisibilityChange::ID)); |
| |
| // No message expected if the renderer already knows the cursor is invisible. |
| sink_->ClearMessages(); |
| cursor_client.HideCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| |
| // Showing the cursor should send a message. |
| sink_->ClearMessages(); |
| cursor_client.ShowCursor(); |
| EXPECT_EQ(1u, sink_->message_count()); |
| EXPECT_TRUE(sink_->GetUniqueMessageMatching( |
| InputMsg_CursorVisibilityChange::ID)); |
| |
| // No messages should be sent while the view is invisible. |
| view_->WasHidden(); |
| sink_->ClearMessages(); |
| cursor_client.HideCursor(); |
| EXPECT_EQ(0u, sink_->message_count()); |
| |
| // Show the view. Since the cursor was visible when the view was hidden, |
| // a message is expected to be sent. |
| sink_->ClearMessages(); |
| view_->WasShown(); |
| EXPECT_TRUE(sink_->GetUniqueMessageMatching( |
| InputMsg_CursorVisibilityChange::ID)); |
| |
| cursor_client.RemoveObserver(view_); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> MakeGLFrame(float scale_factor, |
| gfx::Size size, |
| gfx::Rect damage) { |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| frame->metadata.device_scale_factor = scale_factor; |
| frame->gl_frame_data.reset(new cc::GLFrameData); |
| frame->gl_frame_data->sync_point = 1; |
| memset(frame->gl_frame_data->mailbox.name, '1', 64); |
| frame->gl_frame_data->size = size; |
| frame->gl_frame_data->sub_buffer_rect = damage; |
| return frame.Pass(); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> MakeSoftwareFrame(float scale_factor, |
| gfx::Size size, |
| gfx::Rect damage) { |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| frame->metadata.device_scale_factor = scale_factor; |
| frame->software_frame_data.reset(new cc::SoftwareFrameData); |
| frame->software_frame_data->id = 1; |
| frame->software_frame_data->size = size; |
| frame->software_frame_data->damage_rect = damage; |
| base::SharedMemory shm; |
| shm.CreateAndMapAnonymous(size.GetArea() * 4); |
| shm.GiveToProcess(base::GetCurrentProcessHandle(), |
| &frame->software_frame_data->handle); |
| return frame.Pass(); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor, |
| gfx::Size size, |
| gfx::Rect damage) { |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| frame->metadata.device_scale_factor = scale_factor; |
| frame->delegated_frame_data.reset(new cc::DelegatedFrameData); |
| |
| scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create(); |
| pass->SetNew(cc::RenderPass::Id(1, 1), |
| gfx::Rect(size), |
| gfx::RectF(damage), |
| gfx::Transform()); |
| frame->delegated_frame_data->render_pass_list.push_back(pass.Pass()); |
| return frame.Pass(); |
| } |
| |
| // Resizing in fullscreen mode should send the up-to-date screen info. |
| TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) { |
| aura::RootWindow* root_window = aura_test_helper_->root_window(); |
| root_window->SetLayoutManager(new FullscreenLayoutManager(root_window)); |
| view_->InitAsFullscreen(parent_view_); |
| view_->WasShown(); |
| widget_host_->ResetSizeAndRepaintPendingFlags(); |
| sink_->ClearMessages(); |
| |
| // Call WasResized to flush the old screen info. |
| view_->GetRenderWidgetHost()->WasResized(); |
| { |
| // 0 is CreatingNew message. |
| const IPC::Message* msg = sink_->GetMessageAt(0); |
| EXPECT_EQ(ViewMsg_Resize::ID, msg->type()); |
| ViewMsg_Resize::Param params; |
| ViewMsg_Resize::Read(msg, ¶ms); |
| EXPECT_EQ("0,0 800x600", |
| gfx::Rect(params.a.screen_info.availableRect).ToString()); |
| EXPECT_EQ("800x600", params.a.new_size.ToString()); |
| // Resizes are blocked until we swapped a frame of the correct size, and |
| // we've committed it. |
| view_->OnSwapCompositorFrame( |
| 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size))); |
| ui::DrawWaiterForTest::WaitForCommit(root_window->compositor()); |
| } |
| |
| widget_host_->ResetSizeAndRepaintPendingFlags(); |
| sink_->ClearMessages(); |
| |
| // Make sure the corrent screen size is set along in the resize |
| // request when the screen size has changed. |
| aura_test_helper_->test_screen()->SetUIScale(0.5); |
| EXPECT_EQ(1u, sink_->message_count()); |
| { |
| const IPC::Message* msg = sink_->GetMessageAt(0); |
| EXPECT_EQ(ViewMsg_Resize::ID, msg->type()); |
| ViewMsg_Resize::Param params; |
| ViewMsg_Resize::Read(msg, ¶ms); |
| EXPECT_EQ("0,0 1600x1200", |
| gfx::Rect(params.a.screen_info.availableRect).ToString()); |
| EXPECT_EQ("1600x1200", params.a.new_size.ToString()); |
| view_->OnSwapCompositorFrame( |
| 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size))); |
| ui::DrawWaiterForTest::WaitForCommit(root_window->compositor()); |
| } |
| } |
| |
| // Swapping a frame should notify the window. |
| TEST_F(RenderWidgetHostViewAuraTest, SwapNotifiesWindow) { |
| gfx::Size view_size(100, 100); |
| gfx::Rect view_rect(view_size); |
| |
| view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| view_->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| view_->SetSize(view_size); |
| view_->WasShown(); |
| |
| MockWindowObserver observer; |
| view_->window_->AddObserver(&observer); |
| |
| // Swap a frame through the GPU path. |
| GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; |
| params.surface_id = widget_host_->surface_id(); |
| params.route_id = widget_host_->GetRoutingID(); |
| params.mailbox_name = std::string(64, '1'); |
| params.size = view_size; |
| params.scale_factor = 1.f; |
| |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->AcceleratedSurfaceBuffersSwapped(params, 0); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // DSF = 2 |
| params.size = gfx::Size(200, 200); |
| params.scale_factor = 2.f; |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->AcceleratedSurfaceBuffersSwapped(params, 0); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // Partial frames though GPU path |
| GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params post_params; |
| post_params.surface_id = widget_host_->surface_id(); |
| post_params.route_id = widget_host_->GetRoutingID(); |
| post_params.mailbox_name = std::string(64, '1'); |
| post_params.surface_size = gfx::Size(200, 200); |
| post_params.surface_scale_factor = 2.f; |
| post_params.x = 40; |
| post_params.y = 40; |
| post_params.width = 80; |
| post_params.height = 80; |
| // rect from params is upside down, and is inflated in RWHVA, just because. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, |
| gfx::Rect(19, 39, 42, 42))); |
| view_->AcceleratedSurfacePostSubBuffer(post_params, 0); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // Composite-to-mailbox path |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame(0, MakeGLFrame(1.f, view_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // rect from GL frame is upside down, and is inflated in RWHVA, just because. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, |
| gfx::Rect(4, 89, 7, 7))); |
| view_->OnSwapCompositorFrame( |
| 0, MakeGLFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // Software path |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame(0, MakeSoftwareFrame(1.f, view_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, |
| gfx::Rect(5, 5, 5, 5))); |
| view_->OnSwapCompositorFrame( |
| 0, MakeSoftwareFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| // Delegated renderer path |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, view_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, |
| gfx::Rect(5, 5, 5, 5))); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| view_->window_->RemoveObserver(&observer); |
| } |
| |
| // Skipped frames should not drop their damage. |
| TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) { |
| gfx::Rect view_rect(100, 100); |
| gfx::Size frame_size = view_rect.size(); |
| |
| view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| view_->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| view_->SetSize(view_rect.size()); |
| |
| MockWindowObserver observer; |
| view_->window_->AddObserver(&observer); |
| |
| // A full frame of damage. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // A partial damage frame. |
| gfx::Rect partial_view_rect(30, 30, 20, 20); |
| EXPECT_CALL(observer, |
| OnWindowPaintScheduled(view_->window_, partial_view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // Lock the compositor. Now we should drop frames. |
| view_rect = gfx::Rect(150, 150); |
| view_->SetSize(view_rect.size()); |
| view_->MaybeCreateResizeLock(); |
| |
| // This frame is dropped. |
| gfx::Rect dropped_damage_rect_1(10, 20, 30, 40); |
| EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_1)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| gfx::Rect dropped_damage_rect_2(40, 50, 10, 20); |
| EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_2)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // Unlock the compositor. This frame should damage everything. |
| frame_size = view_rect.size(); |
| |
| gfx::Rect new_damage_rect(5, 6, 10, 10); |
| EXPECT_CALL(observer, |
| OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, new_damage_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // A partial damage frame, this should not be dropped. |
| EXPECT_CALL(observer, |
| OnWindowPaintScheduled(view_->window_, partial_view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| |
| view_->window_->RemoveObserver(&observer); |
| } |
| |
| TEST_F(RenderWidgetHostViewAuraTest, OutputSurfaceIdChange) { |
| gfx::Rect view_rect(100, 100); |
| gfx::Size frame_size = view_rect.size(); |
| |
| view_->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| view_->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| view_->SetSize(view_rect.size()); |
| |
| MockWindowObserver observer; |
| view_->window_->AddObserver(&observer); |
| |
| // Swap a frame. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 0, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // Swap a frame with a different surface id. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // Swap an empty frame, with a different surface id. |
| view_->OnSwapCompositorFrame( |
| 2, MakeDelegatedFrame(1.f, gfx::Size(), gfx::Rect())); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| // Swap another frame, with a different surface id. |
| EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); |
| view_->OnSwapCompositorFrame( |
| 3, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| testing::Mock::VerifyAndClearExpectations(&observer); |
| view_->RunOnCompositingDidCommit(); |
| |
| view_->window_->RemoveObserver(&observer); |
| } |
| |
| TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) { |
| size_t max_renderer_frames = |
| RendererFrameManager::GetInstance()->max_number_of_saved_frames(); |
| ASSERT_LE(2u, max_renderer_frames); |
| size_t renderer_count = max_renderer_frames + 1; |
| gfx::Rect view_rect(100, 100); |
| gfx::Size frame_size = view_rect.size(); |
| |
| scoped_ptr<RenderWidgetHostImpl * []> hosts( |
| new RenderWidgetHostImpl* [renderer_count]); |
| scoped_ptr<FakeRenderWidgetHostViewAura * []> views( |
| new FakeRenderWidgetHostViewAura* [renderer_count]); |
| |
| // Create a bunch of renderers. |
| for (size_t i = 0; i < renderer_count; ++i) { |
| hosts[i] = new RenderWidgetHostImpl( |
| &delegate_, process_host_, MSG_ROUTING_NONE, false); |
| hosts[i]->Init(); |
| hosts[i]->OnMessageReceived( |
| ViewHostMsg_DidActivateAcceleratedCompositing(0, true)); |
| views[i] = new FakeRenderWidgetHostViewAura(hosts[i]); |
| views[i]->InitAsChild(NULL); |
| aura::client::ParentWindowWithContext( |
| views[i]->GetNativeView(), |
| parent_view_->GetNativeView()->GetRootWindow(), |
| gfx::Rect()); |
| views[i]->SetSize(view_rect.size()); |
| } |
| |
| // Make each renderer visible, and swap a frame on it, then make it invisible. |
| for (size_t i = 0; i < renderer_count; ++i) { |
| views[i]->WasShown(); |
| views[i]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| EXPECT_TRUE(views[i]->frame_provider_); |
| views[i]->WasHidden(); |
| } |
| |
| // There should be max_renderer_frames with a frame in it, and one without it. |
| // Since the logic is LRU eviction, the first one should be without. |
| EXPECT_FALSE(views[0]->frame_provider_); |
| for (size_t i = 1; i < renderer_count; ++i) |
| EXPECT_TRUE(views[i]->frame_provider_); |
| |
| // LRU renderer is [0], make it visible, it shouldn't evict anything yet. |
| views[0]->WasShown(); |
| EXPECT_FALSE(views[0]->frame_provider_); |
| EXPECT_TRUE(views[1]->frame_provider_); |
| |
| // Swap a frame on it, it should evict the next LRU [1]. |
| views[0]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| EXPECT_TRUE(views[0]->frame_provider_); |
| EXPECT_FALSE(views[1]->frame_provider_); |
| views[0]->WasHidden(); |
| |
| // LRU renderer is [1], still hidden. Swap a frame on it, it should evict |
| // the next LRU [2]. |
| views[1]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| EXPECT_TRUE(views[0]->frame_provider_); |
| EXPECT_TRUE(views[1]->frame_provider_); |
| EXPECT_FALSE(views[2]->frame_provider_); |
| for (size_t i = 3; i < renderer_count; ++i) |
| EXPECT_TRUE(views[i]->frame_provider_); |
| |
| // Make all renderers but [0] visible and swap a frame on them, keep [0] |
| // hidden, it becomes the LRU. |
| for (size_t i = 1; i < renderer_count; ++i) { |
| views[i]->WasShown(); |
| views[i]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| EXPECT_TRUE(views[i]->frame_provider_); |
| } |
| EXPECT_FALSE(views[0]->frame_provider_); |
| |
| // Swap a frame on [0], it should be evicted immediately. |
| views[0]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| EXPECT_FALSE(views[0]->frame_provider_); |
| |
| // Make [0] visible, and swap a frame on it. Nothing should be evicted |
| // although we're above the limit. |
| views[0]->WasShown(); |
| views[0]->OnSwapCompositorFrame( |
| 1, MakeDelegatedFrame(1.f, frame_size, view_rect)); |
| for (size_t i = 0; i < renderer_count; ++i) |
| EXPECT_TRUE(views[i]->frame_provider_); |
| |
| // Make [0] hidden, it should evict its frame. |
| views[0]->WasHidden(); |
| EXPECT_FALSE(views[0]->frame_provider_); |
| |
| for (size_t i = 0; i < renderer_count; ++i) { |
| views[i]->Destroy(); |
| delete hosts[i]; |
| } |
| } |
| |
| } // namespace content |