diff --git a/DEPS b/DEPS index 9018480b..e43bb64 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '4c6e4103a246c27bdd1302a9c7fba64367758dcc', + 'skia_revision': 'ba8ca0a12985f7c715cb69898fbc60956b65613d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '2783d5390f40a85a6588da2e2f2784fd9349ad86', + 'v8_revision': '7dda9f883e06f3f00bbcec93601877ea7b9b6c65', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other.
diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h index 12d6dfb..48edc36 100644 --- a/base/trace_event/process_memory_totals.h +++ b/base/trace_event/process_memory_totals.h
@@ -43,7 +43,6 @@ uint64_t compressed_bytes = 0; // Linux, Android, ChromeOS - // TODO(hjd): https://crbug.com/707019 uint64_t rss_anon_bytes = 0; uint64_t vm_swap_bytes = 0;
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index a957fa7..0684f0fc 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -592,6 +592,8 @@ "test/layer_tree_pixel_test.h", "test/layer_tree_test.cc", "test/layer_tree_test.h", + "test/mock_compositor_frame_sink_support_client.cc", + "test/mock_compositor_frame_sink_support_client.h", "test/mock_helper.h", "test/mock_occlusion_tracker.h", "test/ordered_simple_task_runner.cc", @@ -857,11 +859,11 @@ "surfaces/display_unittest.cc", "surfaces/referenced_surface_tracker_unittest.cc", "surfaces/surface_aggregator_unittest.cc", - "surfaces/surface_factory_unittest.cc", "surfaces/surface_hittest_unittest.cc", "surfaces/surface_manager_ref_unittest.cc", "surfaces/surface_manager_unittest.cc", "surfaces/surface_sequence_generator_unittest.cc", + "surfaces/surface_synchronization_unittest.cc", "surfaces/surface_unittest.cc", "surfaces/surfaces_pixeltest.cc",
diff --git a/cc/surfaces/BUILD.gn b/cc/surfaces/BUILD.gn index 0ea753ab..154f01e 100644 --- a/cc/surfaces/BUILD.gn +++ b/cc/surfaces/BUILD.gn
@@ -61,9 +61,6 @@ "surface_aggregator.h", "surface_dependency_tracker.cc", "surface_dependency_tracker.h", - "surface_factory.cc", - "surface_factory.h", - "surface_factory_client.h", "surface_hittest.cc", "surface_hittest.h", "surface_hittest_delegate.h",
diff --git a/cc/surfaces/compositor_frame_sink_support.cc b/cc/surfaces/compositor_frame_sink_support.cc index 6caf493..77e317f 100644 --- a/cc/surfaces/compositor_frame_sink_support.cc +++ b/cc/surfaces/compositor_frame_sink_support.cc
@@ -12,6 +12,7 @@ #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/display.h" #include "cc/surfaces/surface.h" +#include "cc/surfaces/surface_info.h" #include "cc/surfaces/surface_manager.h" #include "cc/surfaces/surface_reference.h" @@ -27,8 +28,9 @@ bool needs_sync_points) { std::unique_ptr<CompositorFrameSinkSupport> support = base::WrapUnique(new CompositorFrameSinkSupport( - client, frame_sink_id, is_root, handles_frame_sink_id_invalidation)); - support->Init(surface_manager, needs_sync_points); + client, frame_sink_id, is_root, handles_frame_sink_id_invalidation, + needs_sync_points)); + support->Init(surface_manager); return support; } @@ -43,33 +45,12 @@ reference_tracker_.current_surface_id().is_valid()) RemoveTopLevelRootReference(reference_tracker_.current_surface_id()); - // SurfaceFactory's destructor will attempt to return resources which will - // call back into here and access |client_| so we should destroy - // |surface_factory_|'s resources early on. - surface_factory_->EvictSurface(); + EvictFrame(); surface_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_); if (handles_frame_sink_id_invalidation_) surface_manager_->InvalidateFrameSinkId(frame_sink_id_); } -void CompositorFrameSinkSupport::ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) { - if (!surface_manager_->using_surface_references()) - return; - - SurfaceId last_surface_id = reference_tracker_.current_surface_id(); - - // Populate list of surface references to add and remove based on reference - // surfaces in current frame compared with the last frame. The list of - // surface references includes references from both the pending and active - // frame if any. - reference_tracker_.UpdateReferences(local_surface_id, - active_referenced_surfaces); - - UpdateSurfaceReferences(last_surface_id, local_surface_id); -} - void CompositorFrameSinkSupport::ReturnResources( const ReturnedResourceArray& resources) { if (resources.empty()) @@ -94,8 +75,9 @@ } void CompositorFrameSinkSupport::EvictFrame() { - DCHECK(surface_factory_); - surface_factory_->EvictSurface(); + if (!current_surface_) + return; + DestroyCurrentSurface(); } void CompositorFrameSinkSupport::SetNeedsBeginFrame(bool needs_begin_frame) { @@ -119,7 +101,8 @@ void CompositorFrameSinkSupport::SubmitCompositorFrame( const LocalSurfaceId& local_surface_id, CompositorFrame frame) { - DCHECK(surface_factory_); + TRACE_EVENT0("cc", "CompositorFrameSinkSupport::SubmitCompositorFrame"); + DCHECK(local_surface_id.is_valid()); DCHECK_GE(frame.metadata.begin_frame_ack.sequence_number, BeginFrameArgs::kStartingFrameNumber); DCHECK(!frame.render_pass_list.empty()); @@ -130,13 +113,41 @@ frame.metadata.begin_frame_ack.has_damage = true; BeginFrameAck ack = frame.metadata.begin_frame_ack; - surface_factory_->SubmitCompositorFrame( - local_surface_id, std::move(frame), + + if (!ui::LatencyInfo::Verify(frame.metadata.latency_info, + "RenderWidgetHostImpl::OnSwapCompositorFrame")) { + std::vector<ui::LatencyInfo>().swap(frame.metadata.latency_info); + } + for (ui::LatencyInfo& latency : frame.metadata.latency_info) { + if (latency.latency_components().size() > 0) { + latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, + 0, 0); + } + } + + std::unique_ptr<Surface> surface; + bool create_new_surface = + (!current_surface_ || + local_surface_id != current_surface_->surface_id().local_surface_id()); + if (!create_new_surface) { + surface = std::move(current_surface_); + } else { + surface = CreateSurface(local_surface_id); + } + + surface->QueueFrame( + std::move(frame), base::Bind(&CompositorFrameSinkSupport::DidReceiveCompositorFrameAck, weak_factory_.GetWeakPtr()), base::BindRepeating(&CompositorFrameSinkSupport::WillDrawSurface, weak_factory_.GetWeakPtr())); + if (current_surface_) { + surface->SetPreviousFrameSurface(current_surface_.get()); + DestroyCurrentSurface(); + } + current_surface_ = std::move(surface); + // TODO(eseckler): The CompositorFrame submitted below might not be activated // right away b/c of surface synchronization. We should only send the // BeginFrameAck to DisplayScheduler when it is activated. This also means @@ -191,11 +202,30 @@ surface_manager_->RemoveSurfaceReferences({reference}); } +void CompositorFrameSinkSupport::ReferencedSurfacesChanged( + const LocalSurfaceId& local_surface_id, + const std::vector<SurfaceId>* active_referenced_surfaces) { + if (!surface_manager_->using_surface_references()) + return; + + SurfaceId last_surface_id = reference_tracker_.current_surface_id(); + + // Populate list of surface references to add and remove based on reference + // surfaces in current frame compared with the last frame. The list of + // surface references includes references from both the pending and active + // frame if any. + reference_tracker_.UpdateReferences(local_surface_id, + active_referenced_surfaces); + + UpdateSurfaceReferences(last_surface_id, local_surface_id); +} + void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() { DCHECK_GT(ack_pending_count_, 0); ack_pending_count_--; if (!client_) return; + client_->DidReceiveCompositorFrameAck(surface_returned_resources_); surface_returned_resources_.clear(); } @@ -212,27 +242,41 @@ surface_manager_->AssignTemporaryReference(surface_id, frame_sink_id_); } +void CompositorFrameSinkSupport::ReceiveFromChild( + const TransferableResourceArray& resources) { + surface_resource_holder_.ReceiveFromChild(resources); +} + +void CompositorFrameSinkSupport::RefResources( + const TransferableResourceArray& resources) { + surface_resource_holder_.RefResources(resources); +} + +void CompositorFrameSinkSupport::UnrefResources( + const ReturnedResourceArray& resources) { + surface_resource_holder_.UnrefResources(resources); +} + CompositorFrameSinkSupport::CompositorFrameSinkSupport( CompositorFrameSinkSupportClient* client, const FrameSinkId& frame_sink_id, bool is_root, - bool handles_frame_sink_id_invalidation) + bool handles_frame_sink_id_invalidation, + bool needs_sync_points) : client_(client), frame_sink_id_(frame_sink_id), + surface_resource_holder_(this), reference_tracker_(frame_sink_id), is_root_(is_root), + needs_sync_points_(needs_sync_points), handles_frame_sink_id_invalidation_(handles_frame_sink_id_invalidation), weak_factory_(this) {} -void CompositorFrameSinkSupport::Init(SurfaceManager* surface_manager, - bool needs_sync_points) { +void CompositorFrameSinkSupport::Init(SurfaceManager* surface_manager) { surface_manager_ = surface_manager; - surface_factory_ = base::MakeUnique<SurfaceFactory>( - frame_sink_id_, surface_manager_, this, this); if (handles_frame_sink_id_invalidation_) surface_manager_->RegisterFrameSinkId(frame_sink_id_); surface_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this); - surface_factory_->set_needs_sync_points(needs_sync_points); } void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) { @@ -249,6 +293,41 @@ void CompositorFrameSinkSupport::OnBeginFrameSourcePausedChanged(bool paused) {} +void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) { + DCHECK(surface->HasActiveFrame()); + // TODO(staraz): Notify BeginFrameSource about the last activated sequence + // number. + if (!seen_first_frame_activation_) { + seen_first_frame_activation_ = true; + + const CompositorFrame& frame = surface->GetActiveFrame(); + // CompositorFrames might not be populated with a RenderPass in unit tests. + gfx::Size frame_size; + if (!frame.render_pass_list.empty()) + frame_size = frame.render_pass_list.back()->output_rect.size(); + + // SurfaceCreated only applies for the first Surface activation. Thus, + // SurfaceFactory stops observing new activations after the first one. + surface_manager_->SurfaceCreated(SurfaceInfo( + surface->surface_id(), frame.metadata.device_scale_factor, frame_size)); + } + // Fire SurfaceCreated first so that a temporary reference is added before it + // is potentially transformed into a real reference by the client. + ReferencedSurfacesChanged(surface->surface_id().local_surface_id(), + surface->active_referenced_surfaces()); + if (!surface_manager_->SurfaceModified(surface->surface_id())) { + TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD); + surface->RunDrawCallback(); + } +} + +void CompositorFrameSinkSupport::OnSurfaceDependenciesChanged( + Surface* surface, + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies) {} + +void CompositorFrameSinkSupport::OnSurfaceDiscarded(Surface* surface) {} + void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() { if (!begin_frame_source_) return; @@ -263,10 +342,28 @@ begin_frame_source_->RemoveObserver(this); } +std::unique_ptr<Surface> CompositorFrameSinkSupport::CreateSurface( + const LocalSurfaceId& local_surface_id) { + seen_first_frame_activation_ = false; + std::unique_ptr<Surface> surface = surface_manager_->CreateSurface( + weak_factory_.GetWeakPtr(), local_surface_id); + surface->AddObserver(this); + return surface; +} + +void CompositorFrameSinkSupport::DestroyCurrentSurface() { + current_surface_->RemoveObserver(this); + surface_manager_->DestroySurface(std::move(current_surface_)); +} + void CompositorFrameSinkSupport::RequestCopyOfSurface( - std::unique_ptr<CopyOutputRequest> request) { - DCHECK(surface_factory_); - surface_factory_->RequestCopyOfSurface(std::move(request)); + std::unique_ptr<CopyOutputRequest> copy_request) { + if (!current_surface_) + return; + + DCHECK(current_surface_->compositor_frame_sink_support().get() == this); + current_surface_->RequestCopyOfOutput(std::move(copy_request)); + surface_manager_->SurfaceModified(current_surface_->surface_id()); } } // namespace cc
diff --git a/cc/surfaces/compositor_frame_sink_support.h b/cc/surfaces/compositor_frame_sink_support.h index 247bc1e7..6713fa35 100644 --- a/cc/surfaces/compositor_frame_sink_support.h +++ b/cc/surfaces/compositor_frame_sink_support.h
@@ -14,10 +14,10 @@ #include "cc/output/compositor_frame.h" #include "cc/scheduler/begin_frame_source.h" #include "cc/surfaces/frame_sink_manager_client.h" +#include "cc/surfaces/pending_frame_observer.h" #include "cc/surfaces/referenced_surface_tracker.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_resource_holder.h" #include "cc/surfaces/surface_resource_holder_client.h" #include "cc/surfaces/surfaces_export.h" @@ -27,10 +27,10 @@ class SurfaceManager; class CC_SURFACES_EXPORT CompositorFrameSinkSupport - : public SurfaceFactoryClient, - public BeginFrameObserver, + : public BeginFrameObserver, public SurfaceResourceHolderClient, - public FrameSinkManagerClient { + public FrameSinkManagerClient, + public PendingFrameObserver { public: static std::unique_ptr<CompositorFrameSinkSupport> Create( CompositorFrameSinkSupportClient* client, @@ -44,19 +44,14 @@ const FrameSinkId& frame_sink_id() const { return frame_sink_id_; } - Surface* current_surface_for_testing() { - return surface_factory_->current_surface_for_testing(); - } + Surface* current_surface_for_testing() { return current_surface_.get(); } + SurfaceManager* surface_manager() { return surface_manager_; } + bool needs_sync_points() { return needs_sync_points_; } const ReferencedSurfaceTracker& ReferenceTrackerForTesting() const { return reference_tracker_; } - // SurfaceFactoryClient implementation. - void ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) override; - // SurfaceResourceHolderClient implementation. void ReturnResources(const ReturnedResourceArray& resources) override; @@ -71,13 +66,19 @@ void RequestCopyOfSurface(std::unique_ptr<CopyOutputRequest> request); void ClaimTemporaryReference(const SurfaceId& surface_id); + // TODO(staraz): Move the following 3 methods to private. + void ReceiveFromChild(const TransferableResourceArray& resources); + void RefResources(const TransferableResourceArray& resources); + void UnrefResources(const ReturnedResourceArray& resources); + protected: CompositorFrameSinkSupport(CompositorFrameSinkSupportClient* client, const FrameSinkId& frame_sink_id, bool is_root, - bool handles_frame_sink_id_invalidation); + bool handles_frame_sink_id_invalidation, + bool needs_sync_points); - void Init(SurfaceManager* surface_manager, bool needs_sync_points); + void Init(SurfaceManager* surface_manager); private: // Update surface references with SurfaceManager for current CompositorFrame @@ -89,6 +90,9 @@ void AddTopLevelRootReference(const SurfaceId& surface_id); void RemoveTopLevelRootReference(const SurfaceId& surface_id); + void ReferencedSurfacesChanged( + const LocalSurfaceId& local_surface_id, + const std::vector<SurfaceId>* active_referenced_surfaces); void DidReceiveCompositorFrameAck(); void WillDrawSurface(const LocalSurfaceId& local_surface_id, @@ -99,7 +103,18 @@ const BeginFrameArgs& LastUsedBeginFrameArgs() const override; void OnBeginFrameSourcePausedChanged(bool paused) override; + // PendingFrameObserver implementation. + void OnSurfaceActivated(Surface* surface) override; + void OnSurfaceDependenciesChanged( + Surface* surface, + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies) override; + void OnSurfaceDiscarded(Surface* surface) override; + void UpdateNeedsBeginFramesInternal(); + std::unique_ptr<Surface> CreateSurface( + const LocalSurfaceId& local_surface_id); + void DestroyCurrentSurface(); CompositorFrameSinkSupportClient* const client_; @@ -107,7 +122,9 @@ const FrameSinkId frame_sink_id_; - std::unique_ptr<SurfaceFactory> surface_factory_; + SurfaceResourceHolder surface_resource_holder_; + + std::unique_ptr<Surface> current_surface_; // Counts the number of CompositorFrames that have been submitted and have not // yet received an ACK. int ack_pending_count_ = 0; @@ -130,6 +147,8 @@ ReferencedSurfaceTracker reference_tracker_; const bool is_root_; + const bool needs_sync_points_; + bool seen_first_frame_activation_ = false; // TODO(staraz): Remove this flag once ui::Compositor no longer needs to call // RegisterFrameSinkId().
diff --git a/cc/surfaces/compositor_frame_sink_support_unittest.cc b/cc/surfaces/compositor_frame_sink_support_unittest.cc index 7451f8cb..bdaa1ec 100644 --- a/cc/surfaces/compositor_frame_sink_support_unittest.cc +++ b/cc/surfaces/compositor_frame_sink_support_unittest.cc
@@ -6,13 +6,18 @@ #include "base/macros.h" #include "cc/output/compositor_frame.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" +#include "cc/resources/resource_provider.h" #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/frame_sink_id.h" #include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_info.h" #include "cc/surfaces/surface_manager.h" #include "cc/test/begin_frame_args_test.h" #include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_external_begin_frame_source.h" +#include "cc/test/mock_compositor_frame_sink_support_client.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,1409 +29,761 @@ using testing::Eq; namespace cc { +namespace test { namespace { -constexpr FrameSinkId kDisplayFrameSink(2, 0); -constexpr FrameSinkId kParentFrameSink(3, 0); -constexpr FrameSinkId kChildFrameSink1(65563, 0); -constexpr FrameSinkId kChildFrameSink2(65564, 0); -constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); +constexpr bool kIsRoot = true; +constexpr bool kIsChildRoot = false; +constexpr bool kHandlesFrameSinkIdInvalidation = true; +constexpr bool kNeedsSyncPoints = true; -class MockCompositorFrameSinkSupportClient +constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); +constexpr FrameSinkId kAnotherArbitraryFrameSinkId(2, 2); +constexpr FrameSinkId kYetAnotherArbitraryFrameSinkId(3, 3); + +const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create(); +const base::UnguessableToken kArbitrarySourceId1 = + base::UnguessableToken::Deserialize(0xdead, 0xbeef); +const base::UnguessableToken kArbitrarySourceId2 = + base::UnguessableToken::Deserialize(0xdead, 0xbee0); + +gpu::SyncToken GenTestSyncToken(int id) { + gpu::SyncToken token; + token.Set(gpu::CommandBufferNamespace::GPU_IO, 0, + gpu::CommandBufferId::FromUnsafeValue(id), 1); + return token; +} + +class FakeCompositorFrameSinkSupportClient : public CompositorFrameSinkSupportClient { public: - MockCompositorFrameSinkSupportClient() { - ON_CALL(*this, ReclaimResources(_)) - .WillByDefault(Invoke( - this, - &MockCompositorFrameSinkSupportClient::ReclaimResourcesInternal)); - ON_CALL(*this, DidReceiveCompositorFrameAck(_)) - .WillByDefault(Invoke( - this, - &MockCompositorFrameSinkSupportClient::ReclaimResourcesInternal)); + FakeCompositorFrameSinkSupportClient() = default; + ~FakeCompositorFrameSinkSupportClient() override = default; + + void DidReceiveCompositorFrameAck( + const ReturnedResourceArray& resources) override { + InsertResources(resources); } - ReturnedResourceArray& last_returned_resources() { - return last_returned_resources_; + void OnBeginFrame(const BeginFrameArgs& args) override {} + + void ReclaimResources(const ReturnedResourceArray& resources) override { + InsertResources(resources); } - // CompositorFrameSinkSupportClient implementation. - MOCK_METHOD1(DidReceiveCompositorFrameAck, - void(const ReturnedResourceArray&)); - MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&)); - MOCK_METHOD1(ReclaimResources, void(const ReturnedResourceArray&)); - MOCK_METHOD2(WillDrawSurface, void(const LocalSurfaceId&, const gfx::Rect&)); + void WillDrawSurface(const LocalSurfaceId& local_surface_id, + const gfx::Rect& damage_rect) override {} + + void clear_returned_resources() { returned_resources_.clear(); } + const ReturnedResourceArray& returned_resources() { + return returned_resources_; + } private: - void ReclaimResourcesInternal(const ReturnedResourceArray& resources) { - last_returned_resources_ = resources; + void InsertResources(const ReturnedResourceArray& resources) { + returned_resources_.insert(returned_resources_.end(), resources.begin(), + resources.end()); } - ReturnedResourceArray last_returned_resources_; + ReturnedResourceArray returned_resources_; + + DISALLOW_COPY_AND_ASSIGN(FakeCompositorFrameSinkSupportClient); }; -std::vector<SurfaceId> empty_surface_ids() { - return std::vector<SurfaceId>(); -} - -SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) { - return SurfaceId( - frame_sink_id, - LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); -} - -CompositorFrame MakeCompositorFrame( - std::vector<SurfaceId> activation_dependencies, - std::vector<SurfaceId> referenced_surfaces, - TransferableResourceArray resource_list) { - CompositorFrame compositor_frame = test::MakeCompositorFrame(); - compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true); - compositor_frame.metadata.activation_dependencies = - std::move(activation_dependencies); - compositor_frame.metadata.referenced_surfaces = - std::move(referenced_surfaces); - compositor_frame.resource_list = std::move(resource_list); - return compositor_frame; -} - -CompositorFrame MakeCompositorFrame() { - return MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), - TransferableResourceArray()); -} - -CompositorFrame MakeCompositorFrame( - std::vector<SurfaceId> activation_dependencies) { - return MakeCompositorFrame(activation_dependencies, activation_dependencies, - TransferableResourceArray()); -} - -CompositorFrame MakeCompositorFrame( - std::vector<SurfaceId> activation_dependencies, - std::vector<SurfaceId> referenced_surfaces) { - return MakeCompositorFrame(std::move(activation_dependencies), - std::move(referenced_surfaces), - TransferableResourceArray()); -} - -CompositorFrame MakeCompositorFrameWithResources( - std::vector<SurfaceId> activation_dependencies, - TransferableResourceArray resource_list) { - return MakeCompositorFrame(activation_dependencies, empty_surface_ids(), - std::move(resource_list)); -} - -TransferableResource MakeResource(ResourceId id, - ResourceFormat format, - uint32_t filter, - const gfx::Size& size) { - TransferableResource resource; - resource.id = id; - resource.format = format; - resource.filter = filter; - resource.size = size; - return resource; -} - -} // namespace - class CompositorFrameSinkSupportTest : public testing::Test, public SurfaceObserver { public: CompositorFrameSinkSupportTest() - : surface_manager_(SurfaceManager::LifetimeType::REFERENCES) {} - ~CompositorFrameSinkSupportTest() override {} - - CompositorFrameSinkSupport& display_support() { return *supports_[0]; } - Surface* display_surface() { - return display_support().current_surface_for_testing(); + : support_( + CompositorFrameSinkSupport::Create(&fake_support_client_, + &manager_, + kArbitraryFrameSinkId, + kIsRoot, + kHandlesFrameSinkIdInvalidation, + kNeedsSyncPoints)), + local_surface_id_(3, kArbitraryToken), + frame_sync_token_(GenTestSyncToken(4)), + consumer_sync_token_(GenTestSyncToken(5)) { + manager_.AddObserver(this); + } + ~CompositorFrameSinkSupportTest() override { + manager_.RemoveObserver(this); + support_->EvictFrame(); } - CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } - Surface* parent_surface() { - return parent_support().current_surface_for_testing(); - } - const ReferencedSurfaceTracker& parent_reference_tracker() { - return parent_support().ReferenceTrackerForTesting(); + const SurfaceId& last_created_surface_id() const { + return last_created_surface_id_; } - CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } - Surface* child_surface1() { - return child_support1().current_surface_for_testing(); + // SurfaceObserver implementation. + void OnSurfaceCreated(const SurfaceInfo& surface_info) override { + last_created_surface_id_ = surface_info.id(); + last_surface_info_ = surface_info; + } + void OnSurfaceDamaged(const SurfaceId& id, bool* changed) override { + *changed = true; } - CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } - Surface* child_surface2() { - return child_support2().current_surface_for_testing(); + void SubmitCompositorFrameWithResources(ResourceId* resource_ids, + size_t num_resource_ids) { + CompositorFrame frame = MakeCompositorFrame(); + for (size_t i = 0u; i < num_resource_ids; ++i) { + TransferableResource resource; + resource.id = resource_ids[i]; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + resource.mailbox_holder.sync_token = frame_sync_token_; + frame.resource_list.push_back(resource); + } + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + EXPECT_EQ(last_created_surface_id_.local_surface_id(), local_surface_id_); } - CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } - Surface* surface(int index) { - return support(index).current_surface_for_testing(); + void UnrefResources(ResourceId* ids_to_unref, + int* counts_to_unref, + size_t num_ids_to_unref) { + ReturnedResourceArray unref_array; + for (size_t i = 0; i < num_ids_to_unref; ++i) { + ReturnedResource resource; + resource.sync_token = consumer_sync_token_; + resource.id = ids_to_unref[i]; + resource.count = counts_to_unref[i]; + unref_array.push_back(resource); + } + support_->UnrefResources(unref_array); } - SurfaceManager& surface_manager() { return surface_manager_; } - - // Returns all the references where |surface_id| is the parent. - const SurfaceManager::SurfaceIdSet& GetChildReferences( - const SurfaceId& surface_id) { - return surface_manager().parent_to_child_refs_[surface_id]; + void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids, + int* expected_returned_counts, + size_t expected_resources, + gpu::SyncToken expected_sync_token) { + const ReturnedResourceArray& actual_resources = + fake_support_client_.returned_resources(); + ASSERT_EQ(expected_resources, actual_resources.size()); + for (size_t i = 0; i < expected_resources; ++i) { + ReturnedResource resource = actual_resources[i]; + EXPECT_EQ(expected_sync_token, resource.sync_token); + EXPECT_EQ(expected_returned_ids[i], resource.id); + EXPECT_EQ(expected_returned_counts[i], resource.count); + } + fake_support_client_.clear_returned_resources(); } - // Returns true if there is a temporary reference for |surface_id|. - bool HasTemporaryReference(const SurfaceId& surface_id) { - return surface_manager().HasTemporaryReference(surface_id); - } - - SurfaceDependencyTracker& dependency_tracker() { - return *surface_manager_.dependency_tracker(); - } - - FakeExternalBeginFrameSource* begin_frame_source() { - return begin_frame_source_.get(); - } - - // testing::Test: - void SetUp() override { - testing::Test::SetUp(); - constexpr bool is_root = true; - constexpr bool is_child_root = false; - constexpr bool handles_frame_sink_id_invalidation = true; - constexpr bool needs_sync_points = true; - begin_frame_source_ = - base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); - surface_manager_.SetDependencyTracker( - base::MakeUnique<SurfaceDependencyTracker>(&surface_manager_, - begin_frame_source_.get())); - surface_manager_.AddObserver(this); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kDisplayFrameSink, is_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kParentFrameSink, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kChildFrameSink1, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kChildFrameSink2, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - - // Normally, the BeginFrameSource would be registered by the Display. We - // register it here so that BeginFrames are received by the display support, - // for use in the PassesOnBeginFrameAcks test. Other supports do not receive - // BeginFrames, since the frame sink hierarchy is not set up in this test. - surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), - kDisplayFrameSink); - } - - void TearDown() override { - surface_manager_.RemoveObserver(this); - surface_manager_.SetDependencyTracker(nullptr); - surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); - - // SurfaceDependencyTracker depends on this BeginFrameSource and so it must - // be destroyed AFTER the dependency tracker is destroyed. - begin_frame_source_.reset(); - - supports_.clear(); - - damaged_surfaces_.clear(); - } - - bool IsSurfaceDamaged(const SurfaceId& surface_id) const { - return damaged_surfaces_.count(surface_id) > 0; - } - - // SurfaceObserver implementation: - void OnSurfaceCreated(const SurfaceInfo& surface_info) override {} - void OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) override { - damaged_surfaces_.insert(surface_id); + void RefCurrentFrameResources() { + Surface* surface = manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support_->RefResources(surface->GetActiveFrame().resource_list); } protected: - testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; + SurfaceManager manager_; + FakeCompositorFrameSinkSupportClient fake_support_client_; + std::unique_ptr<CompositorFrameSinkSupport> support_; + LocalSurfaceId local_surface_id_; + SurfaceId last_created_surface_id_; + SurfaceInfo last_surface_info_; - private: - base::flat_set<SurfaceId> damaged_surfaces_; - SurfaceManager surface_manager_; - std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; - std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_; + // This is the sync token submitted with the frame. It should never be + // returned to the client. + const gpu::SyncToken frame_sync_token_; - DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkSupportTest); + // This is the sync token returned by the consumer. It should always be + // returned to the client. + const gpu::SyncToken consumer_sync_token_; }; -// The display root surface should have a surface reference from the top-level -// root added/removed when a CompositorFrame is submitted with a new SurfaceId. -TEST_F(CompositorFrameSinkSupportTest, RootSurfaceReceivesReferences) { - const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); +// Tests submitting a frame with resources followed by one with no resources +// with no resource provider action in between. +TEST_F(CompositorFrameSinkSupportTest, ResourceLifetimeSimple) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - // Submit a CompositorFrame for the first display root surface. - display_support().SubmitCompositorFrame(display_id_first.local_surface_id(), - MakeCompositorFrame()); + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); - // A surface reference from the top-level root is added and there shouldn't be - // a temporary reference. - EXPECT_FALSE(HasTemporaryReference(display_id_first)); - EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), - UnorderedElementsAre(display_id_first)); + // The second frame references no resources of first frame and thus should + // make all resources of first frame available to be returned. + SubmitCompositorFrameWithResources(NULL, 0); - // Submit a CompositorFrame for the second display root surface. - display_support().SubmitCompositorFrame(display_id_second.local_surface_id(), - MakeCompositorFrame()); + ResourceId expected_returned_ids[] = {1, 2, 3}; + int expected_returned_counts[] = {1, 1, 1}; + // Resources were never consumed so no sync token should be set. + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); - // A surface reference from the top-level root to |display_id_second| should - // be added and the reference to |display_root_first| removed. - EXPECT_FALSE(HasTemporaryReference(display_id_second)); - EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), - UnorderedElementsAre(display_id_second)); + ResourceId third_frame_ids[] = {4, 5, 6}; + SubmitCompositorFrameWithResources(third_frame_ids, + arraysize(third_frame_ids)); - // Surface |display_id_first| is unreachable and should get deleted. - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first)); + // All of the resources submitted in the third frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // The forth frame references no resources of third frame and thus should + // make all resources of third frame available to be returned. + ResourceId forth_frame_ids[] = {7, 8, 9}; + SubmitCompositorFrameWithResources(forth_frame_ids, + arraysize(forth_frame_ids)); + + ResourceId forth_expected_returned_ids[] = {4, 5, 6}; + int forth_expected_returned_counts[] = {1, 1, 1}; + // Resources were never consumed so no sync token should be set. + CheckReturnedResourcesMatchExpected( + forth_expected_returned_ids, forth_expected_returned_counts, + arraysize(forth_expected_returned_counts), gpu::SyncToken()); } -// The parent Surface is blocked on |child_id1| and |child_id2|. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingBlockedOnTwo) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids())); - - // parent_support is blocked on |child_id1| and |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1, child_id2)); - - // Submit a CompositorFrame without any dependencies to |child_id1|. - // parent_support should now only be blocked on |child_id2|. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame()); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeCompositorFrame()); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); -} - -// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingBlockedChain) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1}, empty_surface_ids())); - - // parent_support is blocked on |child_id1|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - // The parent should not report damage until it activates. - EXPECT_FALSE(IsSurfaceDamaged(parent_id)); - - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); - - // child_support1 should now be blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - // The parent and child should not report damage until they activate. - EXPECT_FALSE(IsSurfaceDamaged(parent_id)); - EXPECT_FALSE(IsSurfaceDamaged(child_id1)); - - // The parent should still be blocked on |child_id1| because it's pending. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame( - child_id2.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - - // child_surface1 should now be active. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // parent_surface should now be active. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // All three surfaces |parent_id|, |child_id1|, and |child_id2| should - // now report damage. This would trigger a new display frame. - EXPECT_TRUE(IsSurfaceDamaged(parent_id)); - EXPECT_TRUE(IsSurfaceDamaged(child_id1)); - EXPECT_TRUE(IsSurfaceDamaged(child_id2)); -} - -// parent_surface and child_surface1 are blocked on |child_id2|. +// Tests submitting a frame with resources followed by one with no resources +// with the resource provider holding everything alive. TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingTwoBlockedOnOne) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + ResourceLifetimeSimpleWithProviderHoldingAlive) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); - // parent_support is blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); + // Hold on to everything. + RefCurrentFrameResources(); - // child_support1 should now be blocked on |child_id2|. - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); + // The second frame references no resources and thus should make all resources + // available to be returned as soon as the resource provider releases them. + SubmitCompositorFrameWithResources(NULL, 0); - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); - // The parent should still be blocked on |child_id2|. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); + int release_counts[] = {1, 1, 1}; + UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeCompositorFrame()); + // None is returned to the client since DidReceiveCompositorAck is not + // invoked. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); - EXPECT_FALSE(dependency_tracker().has_deadline()); - - // child_surface1 should now be active. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // parent_surface should now be active. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + // Submitting an empty frame causes previous resources referenced by the + // previous frame to be returned to client. + SubmitCompositorFrameWithResources(nullptr, 0); + ResourceId expected_returned_ids[] = {1, 2, 3}; + int expected_returned_counts[] = {1, 1, 1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); } -// parent_surface is blocked on |child_id1|, and child_surface2 is blocked on -// |child_id2| until the deadline hits. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingDeadlineHits) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); +// Tests referencing a resource, unref'ing it to zero, then using it again +// before returning it to the client. +TEST_F(CompositorFrameSinkSupportTest, ResourceReusedBeforeReturn) { + ResourceId first_frame_ids[] = {7}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1}, empty_surface_ids())); + // This removes all references to resource id 7. + SubmitCompositorFrameWithResources(NULL, 0); - // parent_support is blocked on |child_id1|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); + // This references id 7 again. + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); + // This removes it again. + SubmitCompositorFrameWithResources(NULL, 0); - // child_support1 should now be blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); + // Now it should be returned. + // We don't care how many entries are in the returned array for 7, so long as + // the total returned count matches the submitted count. + const ReturnedResourceArray& returned = + fake_support_client_.returned_resources(); + size_t return_count = 0; + for (size_t i = 0; i < returned.size(); ++i) { + EXPECT_EQ(7u, returned[i].id); + return_count += returned[i].count; + } + EXPECT_EQ(2u, return_count); +} - // The parent should still be blocked on |child_id1| because it's pending. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); +// Tests having resources referenced multiple times, as if referenced by +// multiple providers. +TEST_F(CompositorFrameSinkSupportTest, ResourceRefMultipleTimes) { + ResourceId first_frame_ids[] = {3, 4}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + // Ref resources from the first frame twice. + RefCurrentFrameResources(); + RefCurrentFrameResources(); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - // There is still a looming deadline! Eeek! - EXPECT_TRUE(dependency_tracker().has_deadline()); + ResourceId second_frame_ids[] = {4, 5}; + SubmitCompositorFrameWithResources(second_frame_ids, + arraysize(second_frame_ids)); - // parent_support is still blocked on |child_id1|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); + // Ref resources from the second frame 3 times. + RefCurrentFrameResources(); + RefCurrentFrameResources(); + RefCurrentFrameResources(); - // child_support1 is still blocked on |child_id2|. - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); + // Submit a frame with no resources to remove all current frame refs from + // submitted resources. + SubmitCompositorFrameWithResources(NULL, 0); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // Expected current refs: + // 3 -> 2 + // 4 -> 2 + 3 = 5 + // 5 -> 3 + { + SCOPED_TRACE("unref all 3"); + ResourceId ids_to_unref[] = {3, 4, 5}; + int counts[] = {1, 1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); + ResourceId expected_returned_ids[] = {3}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); } - begin_frame_source()->TestOnBeginFrame(args); + // Expected refs remaining: + // 4 -> 3 + // 5 -> 1 + { + SCOPED_TRACE("unref 4 and 5"); + ResourceId ids_to_unref[] = {4, 5}; + int counts[] = {1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); - // The deadline has passed. - EXPECT_FALSE(dependency_tracker().has_deadline()); - - // parent_surface has been activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // child_surface1 has been activated. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); -} - -// Verifies that the deadline does not reset if we submit CompositorFrames -// to new Surfaces with unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingFramesSubmittedAfterDeadlineSet) { - const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); - support(i).SubmitCompositorFrame( - local_surface_id, - MakeCompositorFrame({arbitrary_id}, empty_surface_ids())); - // The deadline has been set. - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // support(i) should be blocked on arbitrary_id. - EXPECT_FALSE(surface(i)->HasActiveFrame()); - EXPECT_TRUE(surface(i)->HasPendingFrame()); - EXPECT_THAT(surface(i)->blocking_surfaces(), - UnorderedElementsAre(arbitrary_id)); - - // Issue a BeginFrame to get closer to the deadline. - begin_frame_source()->TestOnBeginFrame(args); + ResourceId expected_returned_ids[] = {5}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); } - // The deadline hits and all the Surfaces should activate. - begin_frame_source()->TestOnBeginFrame(args); - for (int i = 0; i < 3; ++i) { - EXPECT_TRUE(surface(i)->HasActiveFrame()); - EXPECT_FALSE(surface(i)->HasPendingFrame()); - EXPECT_THAT(surface(i)->blocking_surfaces(), IsEmpty()); + // Now, just 2 refs remaining on resource 4. Unref both at once and make sure + // the returned count is correct. + { + SCOPED_TRACE("unref only 4"); + ResourceId ids_to_unref[] = {4}; + int counts[] = {2}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); + + ResourceId expected_returned_ids[] = {4}; + int expected_returned_counts[] = {2}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); } } -// This test verifies at the Surface activates once a CompositorFrame is -// submitted that has no unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingNewFrameOverridesOldDependencies) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); +TEST_F(CompositorFrameSinkSupportTest, ResourceLifetime) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); - // Submit a CompositorFrame that depends on |arbitrary_id|. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({arbitrary_id}, empty_surface_ids())); + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); - // Verify that the CompositorFrame is blocked on |arbitrary_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(arbitrary_id)); + // The second frame references some of the same resources, but some different + // ones. We expect to receive back resource 1 with a count of 1 since it was + // only referenced by the first frame. + ResourceId second_frame_ids[] = {2, 3, 4}; + SubmitCompositorFrameWithResources(second_frame_ids, + arraysize(second_frame_ids)); + { + SCOPED_TRACE("second frame"); + ResourceId expected_returned_ids[] = {1}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); + } - // Submit a CompositorFrame that has no dependencies. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame()); + // The third frame references a disjoint set of resources, so we expect to + // receive back all resources from the first and second frames. Resource IDs 2 + // and 3 will have counts of 2, since they were used in both frames, and + // resource ID 4 will have a count of 1. + ResourceId third_frame_ids[] = {10, 11, 12, 13}; + SubmitCompositorFrameWithResources(third_frame_ids, + arraysize(third_frame_ids)); - // Verify that the CompositorFrame has been activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + { + SCOPED_TRACE("third frame"); + ResourceId expected_returned_ids[] = {2, 3, 4}; + int expected_returned_counts[] = {2, 2, 1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); + } + + // Simulate a ResourceProvider taking a ref on all of the resources. + RefCurrentFrameResources(); + + ResourceId fourth_frame_ids[] = {12, 13}; + SubmitCompositorFrameWithResources(fourth_frame_ids, + arraysize(fourth_frame_ids)); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + RefCurrentFrameResources(); + + // All resources are still being used by the external reference, so none can + // be returned to the client. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + // Release resources associated with the first RefCurrentFrameResources() call + // first. + { + ResourceId ids_to_unref[] = {10, 11, 12, 13}; + int counts[] = {1, 1, 1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + } + + // Nothing is returned to the client yet since DidReceiveCompositorFrameAck + // is not invoked. + { + SCOPED_TRACE("fourth frame, first unref"); + CheckReturnedResourcesMatchExpected(nullptr, nullptr, 0, + consumer_sync_token_); + } + + { + ResourceId ids_to_unref[] = {12, 13}; + int counts[] = {1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + } + + // Resources 12 and 13 are still in use by the current frame, so they + // shouldn't be available to be returned. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + // If we submit an empty frame, however, they should become available. + // Resources that were previously unref'd also return at this point. + SubmitCompositorFrameWithResources(NULL, 0u); + + { + SCOPED_TRACE("fourth frame, second unref"); + ResourceId expected_returned_ids[] = {10, 11, 12, 13}; + int expected_returned_counts[] = {1, 1, 2, 2}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); + } } -// This test verifies that a pending CompositorFrame does not affect surface -// references. A new surface from a child will continue to exist as a temporary -// reference until the parent's frame activates. -TEST_F(CompositorFrameSinkSupportTest, - OnlyActiveFramesAffectSurfaceReferences) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); +TEST_F(CompositorFrameSinkSupportTest, AddDuringEviction) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(6, kArbitraryToken); + support->SubmitCompositorFrame(local_surface_id, MakeCompositorFrame()); - // child_support1 submits a CompositorFrame without any dependencies. - // DidReceiveCompositorFrameAck should call on immediate activation. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(1); - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame()); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Verify that the child surface is not blocked. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that there's a temporary reference for |child_id1|. - EXPECT_TRUE(HasTemporaryReference(child_id1)); - - // parent_support submits a CompositorFrame that depends on |child_id1| - // (which is already active) and |child_id2|. Thus, the parent should not - // activate immediately. DidReceiveCompositorFrameAck should not be called - // immediately because the parent CompositorFrame is also blocked on - // |child_id2|. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id2}, {child_id1})); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Verify that there's a temporary reference for |child_id1| that still - // exists. - EXPECT_TRUE(HasTemporaryReference(child_id1)); - - // child_support2 submits a CompositorFrame without any dependencies. - // Both the child and the parent should immediately ACK CompositorFrames - // on activation. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); - child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeCompositorFrame()); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Verify that the child surface is not blocked. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent surface's CompositorFrame has activated and that the - // temporary reference has been replaced by a permanent one. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - EXPECT_FALSE(HasTemporaryReference(child_id1)); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(_)) + .WillOnce(testing::InvokeWithoutArgs([&support, &mock_client]() { + LocalSurfaceId new_id(7, base::UnguessableToken::Create()); + support->SubmitCompositorFrame(new_id, MakeCompositorFrame()); + })) + .WillRepeatedly(testing::Return()); + support->EvictFrame(); } -// This test verifies that we do not double count returned resources when a -// CompositorFrame starts out as pending, then becomes active, and then is -// replaced with another active CompositorFrame. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingResourcesOnlyReturnedOnce) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - // The parent submits a CompositorFrame that depends on |child_id| before the - // child submits a CompositorFrame. The CompositorFrame also has resources in - // its resource list. - TransferableResource resource = - MakeResource(1337 /* id */, ALPHA_8 /* format */, 1234 /* filter */, - gfx::Size(1234, 5678)); - TransferableResourceArray resource_list = {resource}; - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrameWithResources({child_id}, resource_list)); - - // Verify that the CompositorFrame is blocked on |child_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id)); - - child_support1().SubmitCompositorFrame( - child_id.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - // Verify that the child CompositorFrame activates immediately. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent has activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // The parent submits a CompositorFrame without any dependencies. That frame - // should activate immediately, replacing the earlier frame. The resource from - // the earlier frame should be returned to the client. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), MakeCompositorFrame({empty_surface_ids()})); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - ReturnedResource returned_resource = resource.ToReturnedResource(); - EXPECT_THAT(support_client_.last_returned_resources(), - UnorderedElementsAre(returned_resource)); -} - -// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. -// child_support1 evicts its blocked Surface. The parent surface should -// activate. -TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceWithPendingFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - // Submit a CompositorFrame that depends on |child_id1|. - parent_support().SubmitCompositorFrame( - parent_id1.local_surface_id(), - MakeCompositorFrame({child_id1}, empty_surface_ids())); - - // Verify that the CompositorFrame is blocked on |child_id1|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // Submit a CompositorFrame that depends on |child_id2|. - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); - - // Verify that the CompositorFrame is blocked on |child_id2|. - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // Evict child_support1's current Surface. - // TODO(fsamuel): EvictFrame => EvictCurrentSurface. - child_support1().EvictFrame(); - - // The parent Surface should immediately activate. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - EXPECT_FALSE(dependency_tracker().has_deadline()); -} - -// This test verifies that if a surface has both a pending and active -// CompositorFrame and the pending CompositorFrame activates, replacing the -// existing active CompositorFrame, then the surface reference hierarchy will be -// updated allowing garbage collection of surfaces that are no longer -// referenced. -TEST_F(CompositorFrameSinkSupportTest, DropStaleReferencesAfterActivation) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - // The parent submits a CompositorFrame that depends on |child_id1| before the - // child submits a CompositorFrame. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1}, empty_surface_ids())); - - // Verify that the CompositorFrame is blocked on |child_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Verify that no references are added while the CompositorFrame is pending. - EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); - - // DidReceiveCompositorFrameAck should get called twice: once for the child - // and once for the now active parent CompositorFrame. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame()); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Verify that the child CompositorFrame activates immediately. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent Surface has activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // Submit a new parent CompositorFrame to add a reference. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), {child_id1})); - - // Verify that the parent Surface has activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // Verify that there is no temporary reference for the child and that - // the reference from the parent to the child still exists. - EXPECT_FALSE(HasTemporaryReference(child_id1)); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); - - // The parent submits another CompositorFrame that depends on |child_id2|. - // Submitting a pending CompositorFrame will not trigger a CompositorFrameAck. - EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id2}, empty_surface_ids())); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // The parent surface should now have both a pending and activate - // CompositorFrame. Verify that the set of child references from - // |parent_id| are only from the active CompositorFrame. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); - - child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the parent Surface has activated and no longer has a pending - // CompositorFrame. Also verify that |child_id1| is no longer a child - // reference of |parent_id|. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - // The parent will not immediately refer to the child until it submits a new - // CompositorFrame with the reference. - EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); -} - -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. No frame has unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with latency info - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); - - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); - - // Verify that the old surface has an active frame and no pending frame. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_FALSE(old_surface->HasPendingFrame()); - - // Submit another frame with some other latency info and a different - // LocalSurfaceId. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); - - CompositorFrame frame2 = MakeCompositorFrame(); - frame2.metadata.latency_info.push_back(info2); - - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - std::move(frame2)); - - // Verify that the new surface has an active frame and no pending frames. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasActiveFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); - - // Verify that the new surface has both latency info elements. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); - - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - - // Two components are the original ones, and the third one is - // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame - // submit. - EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); - - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); - EXPECT_TRUE(aggregated_latency_info.FindLatency( - ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); -} - -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. Old surface has unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with no unresolved dependecy. - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); - - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); - - // Submit a frame with unresolved dependencies. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); - - CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids()); - frame2.metadata.latency_info.push_back(info2); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame2)); - - // Verify that the old surface has both an active and a pending frame. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_TRUE(old_surface->HasPendingFrame()); - - // Submit a frame with a new local surface id. - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the new surface has an active frame only. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasActiveFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); - - // Verify that the new surface has latency info from both active and pending - // frame of the old surface. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); - - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - - // Two components are the original ones, and the third one is - // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame - // submit. - EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); - - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); - EXPECT_TRUE(aggregated_latency_info.FindLatency( - ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); -} - -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. The new surface has unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with no unresolved dependencies. - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); - - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); - - // Verify that the old surface has an active frame only. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_FALSE(old_surface->HasPendingFrame()); - - // Submit a frame with a new local surface id and with unresolved - // dependencies. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); - - CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids()); - frame2.metadata.latency_info.push_back(info2); - - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - std::move(frame2)); - - // Verify that the new surface has a pending frame and no active frame. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasPendingFrame()); - EXPECT_FALSE(surface->HasActiveFrame()); - - // Resolve the dependencies. The frame in parent's surface must become active. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); - EXPECT_TRUE(surface->HasActiveFrame()); - - // Both latency info elements must exist in the now-activated frame of the - // new surface. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); - - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - - // Two components are the original ones, and the third one is - // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame - // submit. - EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); - - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); - EXPECT_TRUE(aggregated_latency_info.FindLatency( - ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); -} - -TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - - // Request BeginFrames. - display_support().SetNeedsBeginFrame(true); - - // Issue a BeginFrame. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - begin_frame_source()->TestOnBeginFrame(args); - - // Check that the support forwards a BeginFrameDidNotSwap ack to the - // BeginFrameSource. - BeginFrameAck ack(0, 1, 1, false); - display_support().BeginFrameDidNotSwap(ack); - EXPECT_EQ(ack, begin_frame_source()->LastAckForObserver(&display_support())); - - // Issue another BeginFrame. - args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); - begin_frame_source()->TestOnBeginFrame(args); - - // Check that the support forwards the BeginFrameAck attached - // to a CompositorFrame to the BeginFrameSource. - BeginFrameAck ack2(0, 2, 2, true); - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.begin_frame_ack = ack2; - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - std::move(frame)); - EXPECT_EQ(ack2, begin_frame_source()->LastAckForObserver(&display_support())); -} +// Tests doing an EvictFrame before shutting down the factory. +TEST_F(CompositorFrameSinkSupportTest, EvictFrame) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + SurfaceId id(kAnotherArbitraryFrameSinkId, local_surface_id); -// Checks that resources and ack are sent together if possible. -TEST_F(CompositorFrameSinkSupportTest, ReturnResourcesWithAck) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); TransferableResource resource; - resource.id = 1234; - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrameWithResources(empty_surface_ids(), {resource})); + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + CompositorFrame frame = MakeCompositorFrame(); + frame.resource_list.push_back(resource); + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); + + ReturnedResourceArray returned_resources = {resource.ToReturnedResource()}; + EXPECT_TRUE(manager_.GetSurfaceForId(id)); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources)) + .Times(1); + support->EvictFrame(); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); +} + +// Tests doing an EvictSurface which has unregistered dependency. +TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceDependencyUnRegistered) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + + TransferableResource resource; + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + CompositorFrame frame = MakeCompositorFrame(); + frame.resource_list.push_back(resource); + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); + + SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id); + Surface* surface = manager_.GetSurfaceForId(surface_id); + surface->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + + ReturnedResourceArray returned_resource = {resource.ToReturnedResource()}; + + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resource)) + .Times(1); + support->EvictFrame(); + EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); +} + +// Tests doing an EvictSurface which has registered dependency. +TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceDependencyRegistered) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + + TransferableResource resource; + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + CompositorFrame frame = MakeCompositorFrame(); + frame.resource_list.push_back(resource); + uint32_t execute_count = 0; + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); + + manager_.RegisterFrameSinkId(kYetAnotherArbitraryFrameSinkId); + + SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id); + Surface* surface = manager_.GetSurfaceForId(surface_id); + surface->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + ReturnedResourceArray returned_resources; - TransferableResource::ReturnResources({resource}, &returned_resources); - EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); - EXPECT_CALL(support_client_, - DidReceiveCompositorFrameAck(Eq(returned_resources))); - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame()); + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + support->EvictFrame(); + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + EXPECT_EQ(0u, execute_count); + + returned_resources.push_back(resource.ToReturnedResource()); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources)) + .Times(1); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); } -// Verifies that if a surface is marked destroyed and a new frame arrives for -// it, it will be recovered. -TEST_F(CompositorFrameSinkSupportTest, SurfaceResurrection) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); +TEST_F(CompositorFrameSinkSupportTest, DestroySequence) { + LocalSurfaceId local_surface_id2(5, kArbitraryToken); + std::unique_ptr<CompositorFrameSinkSupport> support2 = + CompositorFrameSinkSupport::Create( + &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId, + kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2); + support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame()); - // Create the child surface by submitting a frame to it. - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); + // Check that waiting before the sequence is satisfied works. + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + support2->EvictFrame(); - // Verify that the child surface is created. - Surface* surface = surface_manager().GetSurfaceForId(child_id); - EXPECT_NE(nullptr, surface); + DCHECK(manager_.GetSurfaceForId(id2)); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 6)); + DCHECK(!manager_.GetSurfaceForId(id2)); - // Add a reference from the parent to the child. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id})); - - // Attempt to destroy the child surface. The surface must still exist since - // the parent needs it but it will be marked as destroyed. - child_support1().EvictFrame(); - surface = surface_manager().GetSurfaceForId(child_id); - EXPECT_NE(nullptr, surface); - EXPECT_TRUE(surface->destroyed()); - - // Child submits another frame to the same local surface id that is marked - // destroyed. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the surface that was marked destroyed is recovered and is being - // used again. - Surface* surface2 = surface_manager().GetSurfaceForId(child_id); - EXPECT_EQ(surface, surface2); - EXPECT_FALSE(surface2->destroyed()); + // Check that waiting after the sequence is satisfied works. + support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame()); + DCHECK(manager_.GetSurfaceForId(id2)); + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kAnotherArbitraryFrameSinkId, 6)); + support2->EvictFrame(); + DCHECK(!manager_.GetSurfaceForId(id2)); } -// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist -// anymore, it can still be reused for new surfaces. -TEST_F(CompositorFrameSinkSupportTest, LocalSurfaceIdIsReusable) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); +// Tests that Surface ID namespace invalidation correctly allows +// Sequences to be ignored. +TEST_F(CompositorFrameSinkSupportTest, InvalidFrameSinkId) { + FrameSinkId frame_sink_id(1234, 5678); - // Submit the first frame. Creates the surface. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); + LocalSurfaceId local_surface_id(5, kArbitraryToken); + SurfaceId id(support_->frame_sink_id(), local_surface_id); + support_->SubmitCompositorFrame(local_surface_id, MakeCompositorFrame()); - // Add a reference from parent. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id})); + manager_.RegisterFrameSinkId(frame_sink_id); + manager_.GetSurfaceForId(id)->AddDestructionDependency( + SurfaceSequence(frame_sink_id, 4)); - // Remove the reference from parant. This allows us to destroy the surface. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame()); + support_->EvictFrame(); - // Destroy the surface. - child_support1().EvictFrame(); - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); + // Verify the dependency has prevented the surface from getting destroyed. + EXPECT_TRUE(manager_.GetSurfaceForId(id)); - // Submit another frame with the same local surface id. This should work fine - // and a new surface must be created. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); + manager_.InvalidateFrameSinkId(frame_sink_id); + + // Verify that the invalidated namespace caused the unsatisfied sequence + // to be ignored. + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// This test verifies that a crash does not occur if garbage collection is -// triggered during surface dependency resolution. This test triggers garbage -// collection during surface resolution, by causing an activation to remove -// a surface subtree from the root. Both the old subtree and the new -// activated subtree refer to the same dependency. The old subtree was activated -// by deadline, and the new subtree was activated by a dependency finally -// resolving. -TEST_F(CompositorFrameSinkSupportTest, DependencyTrackingGarbageCollection) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - parent_support().SubmitCompositorFrame( - parent_id1.local_surface_id(), - MakeCompositorFrame({child_id}, empty_surface_ids())); - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id1}, empty_surface_ids())); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - - // Advance BeginFrames to trigger a deadline. - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); +TEST_F(CompositorFrameSinkSupportTest, DestroyCycle) { + LocalSurfaceId local_surface_id2(5, kArbitraryToken); + SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2); + std::unique_ptr<CompositorFrameSinkSupport> support2 = + CompositorFrameSinkSupport::Create( + &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId, + kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); + // Give id2 a frame that references local_surface_id_. + { + std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); + CompositorFrame frame = MakeCompositorFrame(); + frame.render_pass_list.push_back(std::move(render_pass)); + frame.metadata.referenced_surfaces.push_back( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support2->SubmitCompositorFrame(local_surface_id2, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id2); } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - - parent_support().SubmitCompositorFrame( - parent_id2.local_surface_id(), - MakeCompositorFrame({child_id}, empty_surface_ids())); - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id2}, empty_surface_ids())); - - // The display surface now has two CompositorFrames. One that is pending, - // indirectly blocked on child_id and one that is active, also indirectly - // referring to child_id, but activated due to the deadline above. - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(display_surface()->HasPendingFrame()); - - // Submitting a CompositorFrame will trigger garbage collection of the - // |parent_id1| subtree. This should not crash. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); -} - -// This test verifies that a crash does not occur if garbage collection is -// triggered when a deadline forces frame activation. This test triggers garbage -// collection during deadline activation by causing the activation of a display -// frame to replace a previously activated display frame that was referring to -// a now-unreachable surface subtree. That subtree gets garbage collected during -// deadline activation. SurfaceDependencyTracker is also tracking a surface -// from that subtree due to an unresolved dependency. This test verifies that -// this dependency resolution does not crash. -TEST_F(CompositorFrameSinkSupportTest, GarbageCollectionOnDeadline) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - // |parent_id1| is blocked on |child_id|. - parent_support().SubmitCompositorFrame( - parent_id1.local_surface_id(), - MakeCompositorFrame({child_id}, empty_surface_ids())); - - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id1})); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - - // Advance BeginFrames to trigger a deadline. This activates the - // CompositorFrame submitted above. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); + support2->EvictFrame(); + // Give local_surface_id_ a frame that references id2. + { + std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); + CompositorFrame frame = MakeCompositorFrame(); + frame.render_pass_list.push_back(std::move(render_pass)); + frame.metadata.referenced_surfaces.push_back(id2); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); + support_->EvictFrame(); + EXPECT_TRUE(manager_.GetSurfaceForId(id2)); + // local_surface_id_ should be retained by reference from id2. + EXPECT_TRUE(manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_))); - // By submitting a display CompositorFrame, and replacing the parent's - // CompositorFrame with another surface ID, parent_id1 becomes unreachable and - // a candidate for garbage collection. - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id2}, empty_surface_ids())); + // Satisfy last destruction dependency for id2. + manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - // Now |parent_id1| is only kept alive by the active |display_id| frame. - parent_support().SubmitCompositorFrame( - parent_id2.local_surface_id(), - MakeCompositorFrame({child_id}, empty_surface_ids())); + // id2 and local_surface_id_ are in a reference cycle that has no surface + // sequences holding on to it, so they should be destroyed. + EXPECT_TRUE(!manager_.GetSurfaceForId(id2)); + EXPECT_TRUE(!manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_))); - // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| - // and |parent_id2|. By activating the pending |display_id| frame by deadline, - // |parent_id1| becomes unreachable and is garbage collected while - // SurfaceDependencyTracker is in the process of activating surfaces. This - // should not cause a crash or use-after-free. - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); + local_surface_id_ = LocalSurfaceId(); +} + +void CopyRequestTestCallback(bool* called, + std::unique_ptr<CopyOutputResult> result) { + *called = true; +} + +TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { + { + std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); + CompositorFrame frame = MakeCompositorFrame(); + frame.render_pass_list.push_back(std::move(render_pass)); + frame.metadata.referenced_surfaces.push_back( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id_); } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); + + bool called1 = false; + std::unique_ptr<CopyOutputRequest> request; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called1)); + request->set_source(kArbitrarySourceId1); + + support_->RequestCopyOfSurface(std::move(request)); + EXPECT_FALSE(called1); + + bool called2 = false; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called2)); + request->set_source(kArbitrarySourceId2); + + support_->RequestCopyOfSurface(std::move(request)); + // Callbacks have different sources so neither should be called. + EXPECT_FALSE(called1); + EXPECT_FALSE(called2); + + bool called3 = false; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called3)); + request->set_source(kArbitrarySourceId1); + + support_->RequestCopyOfSurface(std::move(request)); + // Two callbacks are from source1, so the first should be called. + EXPECT_TRUE(called1); + EXPECT_FALSE(called2); + EXPECT_FALSE(called3); + + support_->EvictFrame(); + local_surface_id_ = LocalSurfaceId(); + EXPECT_TRUE(called1); + EXPECT_TRUE(called2); + EXPECT_TRUE(called3); } -// This test verifies that a CompositorFrame will only blocked on embedded -// surfaces but not on other retained surface IDs in the CompositorFrame. -TEST_F(CompositorFrameSinkSupportTest, OnlyBlockOnEmbeddedSurfaces) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); +// Check whether the SurfaceInfo object is created and populated correctly +// after the frame submission. +TEST_F(CompositorFrameSinkSupportTest, SurfaceInfo) { + CompositorFrame frame = MakeCompositorFrame(); - // Submitting a CompositorFrame with |parent_id2| so that the display - // CompositorFrame can hold a reference to it. - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - MakeCompositorFrame()); + auto render_pass = RenderPass::Create(); + render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(render_pass)); - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id1}, {parent_id2})); + render_pass = RenderPass::Create(); + render_pass->SetNew(2, gfx::Rect(7, 8), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(render_pass)); - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(dependency_tracker().has_deadline()); + frame.metadata.device_scale_factor = 2.5f; - // Verify that the display CompositorFrame will only block on |parent_id1| but - // not |parent_id2|. - EXPECT_THAT(display_surface()->blocking_surfaces(), - UnorderedElementsAre(parent_id1)); - // Verify that the display surface holds no references while its - // CompositorFrame is pending. - EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); - - // Submitting a CompositorFrame with |parent_id1| should unblock the display - // CompositorFrame. - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame()); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty()); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + SurfaceId expected_surface_id(support_->frame_sink_id(), local_surface_id_); + EXPECT_EQ(expected_surface_id, last_surface_info_.id()); + EXPECT_EQ(2.5f, last_surface_info_.device_scale_factor()); + EXPECT_EQ(gfx::Size(7, 8), last_surface_info_.size_in_pixels()); } -// This test verifies that a late arriving CompositorFrame activates immediately -// and does not trigger a new deadline. -TEST_F(CompositorFrameSinkSupportTest, LateArrivingDependency) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); +} // namespace - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id1}, empty_surface_ids())); - - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // Advance BeginFrames to trigger a deadline. This activates the - // CompositorFrame submitted above. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); - - // A late arriving CompositorFrame should activate immediately without - // scheduling a deadline and without waiting for dependencies to resolve. - parent_support().SubmitCompositorFrame( - parent_id1.local_surface_id(), - MakeCompositorFrame({child_id1}, empty_surface_ids())); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); -} - -// This test verifies that CompositorFrames submitted to a surface referenced -// by a parent CompositorFrame as a fallback will be rejected and ACK'ed -// immediately. -TEST_F(CompositorFrameSinkSupportTest, FallbackSurfacesClosed) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - // This is the fallback child surface that the parent holds a reference to. - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - // This is the primary child surface that the parent wants to block on. - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); - - // child_support1 submits a CompositorFrame without any dependencies. - // DidReceiveCompositorFrameAck should call on immediate activation. - // However, resources will not be returned because this frame is a candidate - // for display. - TransferableResource resource = - MakeResource(1337 /* id */, ALPHA_8 /* format */, 1234 /* filter */, - gfx::Size(1234, 5678)); - ReturnedResourceArray returned_resources; - TransferableResource::ReturnResources({resource}, &returned_resources); - - EXPECT_CALL(support_client_, - DidReceiveCompositorFrameAck(Eq(ReturnedResourceArray()))); - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrameWithResources(empty_surface_ids(), {resource})); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // The parent is blocked on |child_id2| and references |child_id1|. The - // surface corresponding to |child_id1| will not accept new CompositorFrames - // while the parent CompositorFrame is blocked. - parent_support().SubmitCompositorFrame( - parent_id1.local_surface_id(), - MakeCompositorFrame({child_id2}, {child_id1})); - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - - // Resources will be returned immediately because |child_id1|'s surface is - // closed. - TransferableResource resource2 = - MakeResource(1246 /* id */, ALPHA_8 /* format */, 1357 /* filter */, - gfx::Size(8765, 4321)); - ReturnedResourceArray returned_resources2; - TransferableResource::ReturnResources({resource2}, &returned_resources2); - EXPECT_CALL(support_client_, - DidReceiveCompositorFrameAck(Eq(returned_resources2))); - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrameWithResources(empty_surface_ids(), {resource2})); - testing::Mock::VerifyAndClearExpectations(&support_client_); - - // Advance BeginFrames to trigger a deadline. This activates the - // CompositorFrame submitted to the parent. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - - // Resources will be returned immediately because |child_id1|'s surface is - // closed forever. - EXPECT_CALL(support_client_, - DidReceiveCompositorFrameAck(Eq(returned_resources2))); - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), - MakeCompositorFrameWithResources(empty_surface_ids(), {resource2})); - testing::Mock::VerifyAndClearExpectations(&support_client_); -} +} // namespace test } // namespace cc
diff --git a/cc/surfaces/direct_compositor_frame_sink.h b/cc/surfaces/direct_compositor_frame_sink.h index 98d4cecd..06ac4c3bb 100644 --- a/cc/surfaces/direct_compositor_frame_sink.h +++ b/cc/surfaces/direct_compositor_frame_sink.h
@@ -13,8 +13,6 @@ #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/display_client.h" #include "cc/surfaces/local_surface_id_allocator.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surfaces_export.h" namespace cc {
diff --git a/cc/surfaces/frame_sink_manager.cc b/cc/surfaces/frame_sink_manager.cc index 7e9095c..c16a921 100644 --- a/cc/surfaces/frame_sink_manager.cc +++ b/cc/surfaces/frame_sink_manager.cc
@@ -9,7 +9,6 @@ #include "base/logging.h" #include "cc/surfaces/frame_sink_manager_client.h" -#include "cc/surfaces/surface_factory_client.h" #if DCHECK_IS_ON() #include <sstream> @@ -28,8 +27,8 @@ FrameSinkManager::FrameSinkManager() {} FrameSinkManager::~FrameSinkManager() { - // All surface factory clients should be unregistered prior to SurfaceManager - // destruction. + // All CompositorFrameSinks should be unregistered prior to + // SurfaceManager destruction. DCHECK_EQ(clients_.size(), 0u); DCHECK_EQ(registered_sources_.size(), 0u); } @@ -207,7 +206,7 @@ } DCHECK(found_child); - // The SurfaceFactoryClient and hierarchy can be registered/unregistered + // The CompositorFrameSinkSupport and hierarchy can be registered/unregistered // in either order, so empty frame_sink_source_map entries need to be // checked when removing either clients or relationships. if (!iter->second.has_children() && !clients_.count(parent_frame_sink_id) &&
diff --git a/cc/surfaces/frame_sink_manager.h b/cc/surfaces/frame_sink_manager.h index b48642f2..0d54a8e3 100644 --- a/cc/surfaces/frame_sink_manager.h +++ b/cc/surfaces/frame_sink_manager.h
@@ -21,7 +21,7 @@ class FrameSinkManagerClient; namespace test { -class CompositorFrameSinkSupportTest; +class SurfaceSynchronizationTest; } class CC_SURFACES_EXPORT FrameSinkManager { @@ -35,8 +35,8 @@ // possibly because a renderer process has crashed. void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id); - // SurfaceFactoryClient, hierarchy, and BeginFrameSource can be registered - // and unregistered in any order with respect to each other. + // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be + // registered and unregistered in any order with respect to each other. // // This happens in practice, e.g. the relationship to between ui::Compositor / // DelegatedFrameHost is known before ui::Compositor has a surface/client). @@ -73,7 +73,7 @@ } private: - friend class test::CompositorFrameSinkSupportTest; + friend class test::SurfaceSynchronizationTest; void RecursivelyAttachBeginFrameSource(const FrameSinkId& frame_sink_id, BeginFrameSource* source); @@ -90,8 +90,9 @@ // considered satisfied. std::unordered_set<FrameSinkId, FrameSinkIdHash> valid_frame_sink_ids_; - // Begin frame source routing. Both BeginFrameSource and SurfaceFactoryClient - // pointers guaranteed alive by callers until unregistered. + // Begin frame source routing. Both BeginFrameSource and + // CompositorFrameSinkSupport pointers guaranteed alive by callers until + // unregistered. struct FrameSinkSourceMapping { FrameSinkSourceMapping(); FrameSinkSourceMapping(const FrameSinkSourceMapping& other);
diff --git a/cc/surfaces/surface.cc b/cc/surfaces/surface.cc index eed066f..3db1e82 100644 --- a/cc/surfaces/surface.cc +++ b/cc/surfaces/surface.cc
@@ -12,10 +12,9 @@ #include "base/stl_util.h" #include "cc/output/compositor_frame.h" #include "cc/output/copy_output_request.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_manager.h" #include "cc/surfaces/surface_resource_holder_client.h" @@ -25,10 +24,12 @@ // completely damaged the first time they're drawn from. static const int kFrameIndexStart = 2; -Surface::Surface(const SurfaceId& id, base::WeakPtr<SurfaceFactory> factory) +Surface::Surface( + const SurfaceId& id, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support) : surface_id_(id), previous_frame_surface_id_(id), - factory_(factory), + compositor_frame_sink_support_(std::move(compositor_frame_sink_support)), frame_index_(kFrameIndexStart), destroyed_(false) {} @@ -57,13 +58,13 @@ } void Surface::QueueFrame(CompositorFrame frame, - const DrawCallback& callback, + const base::Closure& callback, const WillDrawCallback& will_draw_callback) { if (closed_) { - if (factory_ && factory_->resource_holder_client()) { + if (compositor_frame_sink_support_) { ReturnedResourceArray resources; TransferableResource::ReturnResources(frame.resource_list, &resources); - factory_->resource_holder_client()->ReturnResources(resources); + compositor_frame_sink_support_->ReturnResources(resources); } callback.Run(); return; @@ -79,7 +80,7 @@ // Receive and track the resources referenced from the CompositorFrame // regardless of whether it's pending or active. - factory_->ReceiveFromChild(frame.resource_list); + compositor_frame_sink_support_->ReceiveFromChild(frame.resource_list); bool is_pending_frame = !blocking_surfaces_.empty(); @@ -98,7 +99,9 @@ bool is_fallback_surface = frame_sink_ids_for_dependencies.count(surface_id.frame_sink_id()) > 0; if (is_fallback_surface) { - Surface* surface = factory_->manager()->GetSurfaceForId(surface_id); + Surface* surface = + compositor_frame_sink_support_->surface_manager()->GetSurfaceForId( + surface_id); DCHECK(surface); surface->Close(); } @@ -107,7 +110,8 @@ FrameData(std::move(frame), callback, will_draw_callback); // Ask the surface manager to inform |this| when its dependencies are // resolved. - factory_->manager()->RequestSurfaceResolution(this); + compositor_frame_sink_support_->surface_manager()->RequestSurfaceResolution( + this); } else { // If there are no blockers, then immediately activate the frame. ActivateFrame(FrameData(std::move(frame), callback, will_draw_callback)); @@ -175,7 +179,7 @@ } Surface::FrameData::FrameData(CompositorFrame&& frame, - const DrawCallback& draw_callback, + const base::Closure& draw_callback, const WillDrawCallback& will_draw_callback) : frame(std::move(frame)), draw_callback(draw_callback), @@ -197,7 +201,7 @@ // deadline has hit and the frame was forcibly activated by the display // compositor. void Surface::ActivateFrame(FrameData frame_data) { - DCHECK(factory_); + DCHECK(compositor_frame_sink_support_); // Save root pass copy requests. std::vector<std::unique_ptr<CopyOutputRequest>> old_copy_requests; @@ -231,7 +235,8 @@ const CompositorFrame& current_frame) { // If there is no SurfaceDependencyTracker installed then the |current_frame| // does not block on anything. - if (!factory_->manager()->dependency_tracker()) { + if (!compositor_frame_sink_support_->surface_manager() + ->dependency_tracker()) { blocking_surfaces_.clear(); return; } @@ -240,7 +245,9 @@ for (const SurfaceId& surface_id : current_frame.metadata.activation_dependencies) { - Surface* surface = factory_->manager()->GetSurfaceForId(surface_id); + Surface* surface = + compositor_frame_sink_support_->surface_manager()->GetSurfaceForId( + surface_id); // If a referenced surface does not have a corresponding active frame in the // display compositor, then it blocks this frame. if (!surface || !surface->HasActiveFrame()) @@ -308,8 +315,8 @@ void Surface::RunDrawCallback() { if (active_frame_data_ && !active_frame_data_->draw_callback.is_null()) { - DrawCallback callback = active_frame_data_->draw_callback; - active_frame_data_->draw_callback = DrawCallback(); + base::Closure callback = active_frame_data_->draw_callback; + active_frame_data_->draw_callback = base::Closure(); callback.Run(); } } @@ -338,7 +345,7 @@ void Surface::UnrefFrameResourcesAndRunDrawCallback( base::Optional<FrameData> frame_data) { - if (!frame_data || !factory_) + if (!frame_data || !compositor_frame_sink_support_) return; ReturnedResourceArray resources; @@ -347,7 +354,7 @@ // No point in returning same sync token to sender. for (auto& resource : resources) resource.sync_token.Clear(); - factory_->UnrefResources(resources); + compositor_frame_sink_support_->UnrefResources(resources); if (!frame_data->draw_callback.is_null()) frame_data->draw_callback.Run();
diff --git a/cc/surfaces/surface.h b/cc/surfaces/surface.h index 63371d6..08addef 100644 --- a/cc/surfaces/surface.h +++ b/cc/surfaces/surface.h
@@ -19,9 +19,9 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "cc/output/copy_output_request.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/frame_sink_id.h" #include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_id.h" #include "cc/surfaces/surface_sequence.h" #include "cc/surfaces/surfaces_export.h" @@ -35,14 +35,15 @@ class CompositorFrame; class CopyOutputRequest; -class SurfaceFactory; class CC_SURFACES_EXPORT Surface { public: - using DrawCallback = SurfaceFactory::DrawCallback; - using WillDrawCallback = SurfaceFactory::WillDrawCallback; + using WillDrawCallback = + base::RepeatingCallback<void(const LocalSurfaceId&, const gfx::Rect&)>; - Surface(const SurfaceId& id, base::WeakPtr<SurfaceFactory> factory); + Surface( + const SurfaceId& id, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support); ~Surface(); const SurfaceId& surface_id() const { return surface_id_; } @@ -58,7 +59,7 @@ // |will_draw_callback| is called when |surface| is scheduled for a draw and // there is visible damage. void QueueFrame(CompositorFrame frame, - const DrawCallback& draw_callback, + const base::Closure& draw_callback, const WillDrawCallback& will_draw_callback); void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> copy_request); @@ -93,7 +94,9 @@ void RunDrawCallback(); void RunWillDrawCallback(const gfx::Rect& damage_rect); - base::WeakPtr<SurfaceFactory> factory() { return factory_; } + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support() { + return compositor_frame_sink_support_; + } // Add a SurfaceSequence that must be satisfied before the Surface is // destroyed. @@ -127,13 +130,13 @@ private: struct FrameData { FrameData(CompositorFrame&& frame, - const DrawCallback& draw_callback, + const base::Closure& draw_callback, const WillDrawCallback& will_draw_callback); FrameData(FrameData&& other); ~FrameData(); FrameData& operator=(FrameData&& other); CompositorFrame frame; - DrawCallback draw_callback; + base::Closure draw_callback; WillDrawCallback will_draw_callback; }; @@ -159,7 +162,7 @@ const SurfaceId surface_id_; SurfaceId previous_frame_surface_id_; - base::WeakPtr<SurfaceFactory> factory_; + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support_; base::Optional<FrameData> pending_frame_data_; base::Optional<FrameData> active_frame_data_;
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc index b9a679f..93530ff6 100644 --- a/cc/surfaces/surface_aggregator.cc +++ b/cc/surfaces/surface_aggregator.cc
@@ -25,6 +25,7 @@ #include "cc/quads/surface_draw_quad.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/resource_provider.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_manager.h" #include "cc/trees/blocking_task_runner.h" @@ -120,11 +121,12 @@ return out_clip; } -static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory, - const ReturnedResourceArray& resources, - BlockingTaskRunner* main_thread_task_runner) { - if (surface_factory) - surface_factory->UnrefResources(resources); +static void UnrefHelper( + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, + const ReturnedResourceArray& resources, + BlockingTaskRunner* main_thread_task_runner) { + if (compositor_frame_sink_support) + compositor_frame_sink_support->UnrefResources(resources); } int SurfaceAggregator::RemapPassId(int surface_local_pass_id, @@ -145,11 +147,12 @@ int SurfaceAggregator::ChildIdForSurface(Surface* surface) { auto it = surface_id_to_resource_child_id_.find(surface->surface_id()); if (it == surface_id_to_resource_child_id_.end()) { - int child_id = - provider_->CreateChild(base::Bind(&UnrefHelper, surface->factory())); - if (surface->factory()) { + int child_id = provider_->CreateChild( + base::Bind(&UnrefHelper, surface->compositor_frame_sink_support())); + if (surface->compositor_frame_sink_support()) { provider_->SetChildNeedsSyncTokens( - child_id, surface->factory()->needs_sync_points()); + child_id, + surface->compositor_frame_sink_support()->needs_sync_points()); } surface_id_to_resource_child_id_[surface->surface_id()] = child_id; return child_id; @@ -609,8 +612,9 @@ // TODO(jbauman): hack for unit tests that don't set up rp if (provider_) { child_id = ChildIdForSurface(surface); - if (surface->factory()) - surface->factory()->RefResources(frame.resource_list); + if (surface->compositor_frame_sink_support()) + surface->compositor_frame_sink_support()->RefResources( + frame.resource_list); provider_->ReceiveFromChild(child_id, frame.resource_list); } CHECK(debug_weak_this.get());
diff --git a/cc/surfaces/surface_factory.cc b/cc/surfaces/surface_factory.cc deleted file mode 100644 index e4fd41e7..0000000 --- a/cc/surfaces/surface_factory.cc +++ /dev/null
@@ -1,154 +0,0 @@ -// 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 "cc/surfaces/surface_factory.h" - -#include <utility> - -#include "base/memory/ptr_util.h" -#include "base/trace_event/trace_event.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/copy_output_request.h" -#include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" -#include "cc/surfaces/surface_info.h" -#include "cc/surfaces/surface_manager.h" -#include "ui/gfx/geometry/size.h" - -namespace cc { -SurfaceFactory::SurfaceFactory( - const FrameSinkId& frame_sink_id, - SurfaceManager* manager, - SurfaceFactoryClient* client, - SurfaceResourceHolderClient* resource_holder_client) - : frame_sink_id_(frame_sink_id), - manager_(manager), - client_(client), - resource_holder_client_(resource_holder_client), - holder_(resource_holder_client), - needs_sync_points_(true), - weak_factory_(this) {} - -SurfaceFactory::~SurfaceFactory() { - // This is to prevent troubles when a factory that resides in a client is - // being destroyed. In such cases, the factory might attempt to return - // resources to the client while it's in the middle of destruction and this - // could cause a crash or some unexpected behaviour. - DCHECK(!current_surface_) << "Please call EvictSurface before destruction"; -} - -void SurfaceFactory::EvictSurface() { - if (!current_surface_) - return; - Destroy(std::move(current_surface_)); -} - -void SurfaceFactory::SubmitCompositorFrame( - const LocalSurfaceId& local_surface_id, - CompositorFrame frame, - const DrawCallback& callback, - const WillDrawCallback& will_draw_callback) { - TRACE_EVENT0("cc", "SurfaceFactory::SubmitCompositorFrame"); - DCHECK(local_surface_id.is_valid()); - DCHECK(!frame.render_pass_list.empty()); - - if (!ui::LatencyInfo::Verify(frame.metadata.latency_info, - "RenderWidgetHostImpl::OnSwapCompositorFrame")) { - std::vector<ui::LatencyInfo>().swap(frame.metadata.latency_info); - } - - for (ui::LatencyInfo& latency : frame.metadata.latency_info) { - if (latency.latency_components().size() > 0) { - latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, - 0, 0); - } - } - - std::unique_ptr<Surface> surface; - bool create_new_surface = - (!current_surface_ || - local_surface_id != current_surface_->surface_id().local_surface_id()); - if (!create_new_surface) { - surface = std::move(current_surface_); - } else { - surface = Create(local_surface_id); - } - surface->QueueFrame(std::move(frame), callback, will_draw_callback); - - if (current_surface_ && create_new_surface) { - surface->SetPreviousFrameSurface(current_surface_.get()); - Destroy(std::move(current_surface_)); - } - current_surface_ = std::move(surface); -} - -void SurfaceFactory::RequestCopyOfSurface( - std::unique_ptr<CopyOutputRequest> copy_request) { - if (!current_surface_) { - copy_request->SendEmptyResult(); - return; - } - DCHECK(current_surface_->factory().get() == this); - current_surface_->RequestCopyOfOutput(std::move(copy_request)); - manager_->SurfaceModified(current_surface_->surface_id()); -} - -void SurfaceFactory::ReceiveFromChild( - const TransferableResourceArray& resources) { - holder_.ReceiveFromChild(resources); -} - -void SurfaceFactory::RefResources(const TransferableResourceArray& resources) { - holder_.RefResources(resources); -} - -void SurfaceFactory::UnrefResources(const ReturnedResourceArray& resources) { - holder_.UnrefResources(resources); -} - -void SurfaceFactory::OnSurfaceActivated(Surface* surface) { - DCHECK(surface->HasActiveFrame()); - if (!seen_first_frame_activation_) { - seen_first_frame_activation_ = true; - - const CompositorFrame& frame = surface->GetActiveFrame(); - gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size(); - - // SurfaceCreated only applies for the first Surface activation. Thus, - // SurfaceFactory stops observing new activations after the first one. - manager_->SurfaceCreated(SurfaceInfo( - surface->surface_id(), frame.metadata.device_scale_factor, frame_size)); - } - // Fire SurfaceCreated first so that a temporary reference is added before it - // is potentially transformed into a real reference by the client. - client_->ReferencedSurfacesChanged(surface->surface_id().local_surface_id(), - surface->active_referenced_surfaces()); - if (!manager_->SurfaceModified(surface->surface_id())) { - TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD); - surface->RunDrawCallback(); - } -} - -void SurfaceFactory::OnSurfaceDependenciesChanged( - Surface* surface, - const base::flat_set<SurfaceId>& added_dependencies, - const base::flat_set<SurfaceId>& removed_dependencies) {} - -void SurfaceFactory::OnSurfaceDiscarded(Surface* surface) {} - -std::unique_ptr<Surface> SurfaceFactory::Create( - const LocalSurfaceId& local_surface_id) { - seen_first_frame_activation_ = false; - std::unique_ptr<Surface> surface = - manager_->CreateSurface(weak_factory_.GetWeakPtr(), local_surface_id); - surface->AddObserver(this); - return surface; -} - -void SurfaceFactory::Destroy(std::unique_ptr<Surface> surface) { - surface->RemoveObserver(this); - manager_->DestroySurface(std::move(surface)); -} - -} // namespace cc
diff --git a/cc/surfaces/surface_factory.h b/cc/surfaces/surface_factory.h deleted file mode 100644 index 5c31eab..0000000 --- a/cc/surfaces/surface_factory.h +++ /dev/null
@@ -1,115 +0,0 @@ -// 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. - -#ifndef CC_SURFACES_SURFACE_FACTORY_H_ -#define CC_SURFACES_SURFACE_FACTORY_H_ - -#include <memory> -#include <set> - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/observer_list.h" -#include "cc/output/compositor_frame.h" -#include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_id.h" -#include "cc/surfaces/surface_resource_holder.h" -#include "cc/surfaces/surface_sequence.h" -#include "cc/surfaces/surfaces_export.h" - -namespace cc { -class CopyOutputRequest; -class Surface; -class SurfaceFactoryClient; -class SurfaceManager; - -// This class is used for creating surfaces and submitting compositor frames to -// them. Surfaces are created lazily each time SubmitCompositorFrame is -// called with a local frame id that is different from the last call. Only one -// surface is owned by this class at a time, and upon constructing a new surface -// the old one will be destructed. Resources submitted to surfaces created by a -// particular factory will be returned to that factory's client when they are no -// longer being used. This is the only class most users of surfaces will need to -// directly interact with. -class CC_SURFACES_EXPORT SurfaceFactory : public PendingFrameObserver { - public: - using DrawCallback = base::Callback<void()>; - using WillDrawCallback = - base::RepeatingCallback<void(const LocalSurfaceId&, const gfx::Rect&)>; - - SurfaceFactory(const FrameSinkId& frame_sink_id, - SurfaceManager* manager, - SurfaceFactoryClient* client, - SurfaceResourceHolderClient* resource_holder_client); - ~SurfaceFactory() override; - - const FrameSinkId& frame_sink_id() const { return frame_sink_id_; } - - // Destroys the current surface. You need to call this method before the - // factory is destroyed, or when you would like to get rid of the surface as - // soon as possible (otherwise, the next time you call SubmitCompositorFrame - // the old surface will be dealt with). - void EvictSurface(); - - // Submits the frame to the current surface being managed by the factory if - // the local frame ids match, or creates a new surface with the given local - // frame id, destroys the old one, and submits the frame to this new surface. - // The frame can contain references to any surface, regardless of which - // factory owns it. The callback is called the first time this frame is used - // to draw, or if the frame is discarded. - void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id, - CompositorFrame frame, - const DrawCallback& callback, - const WillDrawCallback& will_draw_callback); - void RequestCopyOfSurface(std::unique_ptr<CopyOutputRequest> copy_request); - - SurfaceFactoryClient* client() { return client_; } - - SurfaceResourceHolderClient* resource_holder_client() { - return resource_holder_client_; - } - - void ReceiveFromChild(const TransferableResourceArray& resources); - void RefResources(const TransferableResourceArray& resources); - void UnrefResources(const ReturnedResourceArray& resources); - - SurfaceManager* manager() { return manager_; } - - Surface* current_surface_for_testing() { return current_surface_.get(); } - - // This can be set to false if resources from this SurfaceFactory don't need - // to have sync points set on them when returned from the Display, for - // example if the Display shares a context with the creator. - bool needs_sync_points() const { return needs_sync_points_; } - void set_needs_sync_points(bool needs) { needs_sync_points_ = needs; } - - private: - // PendingFrameObserver implementation. - void OnSurfaceActivated(Surface* surface) override; - void OnSurfaceDependenciesChanged( - Surface* surface, - const base::flat_set<SurfaceId>& added_dependencies, - const base::flat_set<SurfaceId>& removed_dependencies) override; - void OnSurfaceDiscarded(Surface* surface) override; - - std::unique_ptr<Surface> Create(const LocalSurfaceId& local_surface_id); - void Destroy(std::unique_ptr<Surface> surface); - - const FrameSinkId frame_sink_id_; - SurfaceManager* manager_; - SurfaceFactoryClient* client_; - SurfaceResourceHolderClient* resource_holder_client_; - SurfaceResourceHolder holder_; - bool needs_sync_points_; - bool seen_first_frame_activation_ = false; - std::unique_ptr<Surface> current_surface_; - base::WeakPtrFactory<SurfaceFactory> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(SurfaceFactory); -}; - -} // namespace cc - -#endif // CC_SURFACES_SURFACE_FACTORY_H_
diff --git a/cc/surfaces/surface_factory_client.h b/cc/surfaces/surface_factory_client.h deleted file mode 100644 index bbcf545b..0000000 --- a/cc/surfaces/surface_factory_client.h +++ /dev/null
@@ -1,28 +0,0 @@ -// 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. - -#ifndef CC_SURFACES_SURFACE_FACTORY_CLIENT_H_ -#define CC_SURFACES_SURFACE_FACTORY_CLIENT_H_ - -#include "cc/resources/returned_resource.h" -#include "cc/surfaces/local_surface_id.h" -#include "cc/surfaces/surfaces_export.h" -#include "ui/gfx/geometry/rect.h" - -namespace cc { - -class SurfaceId; - -class CC_SURFACES_EXPORT SurfaceFactoryClient { - public: - virtual ~SurfaceFactoryClient() = default; - - virtual void ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) = 0; -}; - -} // namespace cc - -#endif // CC_SURFACES_SURFACE_FACTORY_CLIENT_H_
diff --git a/cc/surfaces/surface_factory_unittest.cc b/cc/surfaces/surface_factory_unittest.cc deleted file mode 100644 index 6d7d30b7..0000000 --- a/cc/surfaces/surface_factory_unittest.cc +++ /dev/null
@@ -1,769 +0,0 @@ -// 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 "cc/surfaces/surface_factory.h" - -#include <stddef.h> -#include <stdint.h> - -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/macros.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/copy_output_request.h" -#include "cc/output/copy_output_result.h" -#include "cc/resources/resource_provider.h" -#include "cc/surfaces/frame_sink_manager_client.h" -#include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" -#include "cc/surfaces/surface_info.h" -#include "cc/surfaces/surface_manager.h" -#include "cc/surfaces/surface_resource_holder_client.h" -#include "cc/test/compositor_frame_helpers.h" -#include "cc/test/fake_surface_resource_holder_client.h" -#include "cc/test/scheduler_test_common.h" -#include "cc/test/stub_surface_factory_client.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/geometry/size.h" - -namespace cc { -namespace { - -static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); -static constexpr FrameSinkId kAnotherArbitraryFrameSinkId(2, 2); -static const base::UnguessableToken kArbitraryToken = - base::UnguessableToken::Create(); -static auto kArbitrarySourceId1 = - base::UnguessableToken::Deserialize(0xdead, 0xbeef); -static auto kArbitrarySourceId2 = - base::UnguessableToken::Deserialize(0xdead, 0xbee0); - -gpu::SyncToken GenTestSyncToken(int id) { - gpu::SyncToken token; - token.Set(gpu::CommandBufferNamespace::GPU_IO, 0, - gpu::CommandBufferId::FromUnsafeValue(id), 1); - return token; -} - -class SurfaceFactoryTest : public testing::Test, public SurfaceObserver { - public: - SurfaceFactoryTest() - : factory_(new SurfaceFactory(kArbitraryFrameSinkId, - &manager_, - &stub_surface_factory_client_, - &fake_surface_resource_holder_client_)), - local_surface_id_(3, kArbitraryToken), - frame_sync_token_(GenTestSyncToken(4)), - consumer_sync_token_(GenTestSyncToken(5)) { - manager_.AddObserver(this); - } - - const SurfaceId& last_created_surface_id() const { - return last_created_surface_id_; - } - - // SurfaceObserver implementation. - void OnSurfaceCreated(const SurfaceInfo& surface_info) override { - EXPECT_EQ(kArbitraryFrameSinkId, surface_info.id().frame_sink_id()); - last_created_surface_id_ = surface_info.id(); - last_surface_info_ = surface_info; - } - - void OnSurfaceDamaged(const SurfaceId& id, bool* changed) override { - *changed = true; - } - - ~SurfaceFactoryTest() override { - manager_.RemoveObserver(this); - factory_->EvictSurface(); - } - - void SubmitCompositorFrameWithResources(ResourceId* resource_ids, - size_t num_resource_ids) { - CompositorFrame frame = test::MakeCompositorFrame(); - for (size_t i = 0u; i < num_resource_ids; ++i) { - TransferableResource resource; - resource.id = resource_ids[i]; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - resource.mailbox_holder.sync_token = frame_sync_token_; - frame.resource_list.push_back(resource); - } - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id_.local_surface_id(), local_surface_id_); - } - - void UnrefResources(ResourceId* ids_to_unref, - int* counts_to_unref, - size_t num_ids_to_unref) { - ReturnedResourceArray unref_array; - for (size_t i = 0; i < num_ids_to_unref; ++i) { - ReturnedResource resource; - resource.sync_token = consumer_sync_token_; - resource.id = ids_to_unref[i]; - resource.count = counts_to_unref[i]; - unref_array.push_back(resource); - } - factory_->UnrefResources(unref_array); - } - - void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids, - int* expected_returned_counts, - size_t expected_resources, - gpu::SyncToken expected_sync_token) { - const ReturnedResourceArray& actual_resources = - fake_surface_resource_holder_client_.returned_resources(); - ASSERT_EQ(expected_resources, actual_resources.size()); - for (size_t i = 0; i < expected_resources; ++i) { - ReturnedResource resource = actual_resources[i]; - EXPECT_EQ(expected_sync_token, resource.sync_token); - EXPECT_EQ(expected_returned_ids[i], resource.id); - EXPECT_EQ(expected_returned_counts[i], resource.count); - } - fake_surface_resource_holder_client_.clear_returned_resources(); - } - - void RefCurrentFrameResources() { - Surface* surface = manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory_->RefResources(surface->GetActiveFrame().resource_list); - } - - protected: - SurfaceManager manager_; - StubSurfaceFactoryClient stub_surface_factory_client_; - FakeSurfaceResourceHolderClient fake_surface_resource_holder_client_; - std::unique_ptr<SurfaceFactory> factory_; - LocalSurfaceId local_surface_id_; - SurfaceId last_created_surface_id_; - SurfaceInfo last_surface_info_; - - // This is the sync token submitted with the frame. It should never be - // returned to the client. - const gpu::SyncToken frame_sync_token_; - - // This is the sync token returned by the consumer. It should always be - // returned to the client. - const gpu::SyncToken consumer_sync_token_; -}; - -// Tests submitting a frame with resources followed by one with no resources -// with no resource provider action in between. -TEST_F(SurfaceFactoryTest, ResourceLifetimeSimple) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - // The second frame references no resources of first frame and thus should - // make all resources of first frame available to be returned. - SubmitCompositorFrameWithResources(NULL, 0); - - ResourceId expected_returned_ids[] = {1, 2, 3}; - int expected_returned_counts[] = {1, 1, 1}; - // Resources were never consumed so no sync token should be set. - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - - ResourceId third_frame_ids[] = {4, 5, 6}; - SubmitCompositorFrameWithResources(third_frame_ids, - arraysize(third_frame_ids)); - - // All of the resources submitted in the third frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - // The forth frame references no resources of third frame and thus should - // make all resources of third frame available to be returned. - ResourceId forth_frame_ids[] = {7, 8, 9}; - SubmitCompositorFrameWithResources(forth_frame_ids, - arraysize(forth_frame_ids)); - - ResourceId forth_expected_returned_ids[] = {4, 5, 6}; - int forth_expected_returned_counts[] = {1, 1, 1}; - // Resources were never consumed so no sync token should be set. - CheckReturnedResourcesMatchExpected( - forth_expected_returned_ids, forth_expected_returned_counts, - arraysize(forth_expected_returned_counts), gpu::SyncToken()); -} - -// Tests submitting a frame with resources followed by one with no resources -// with the resource provider holding everything alive. -TEST_F(SurfaceFactoryTest, ResourceLifetimeSimpleWithProviderHoldingAlive) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - // Hold on to everything. - RefCurrentFrameResources(); - - // The second frame references no resources and thus should make all resources - // available to be returned as soon as the resource provider releases them. - SubmitCompositorFrameWithResources(NULL, 0); - - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - int release_counts[] = {1, 1, 1}; - UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); - - ResourceId expected_returned_ids[] = {1, 2, 3}; - int expected_returned_counts[] = {1, 1, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); -} - -// Tests referencing a resource, unref'ing it to zero, then using it again -// before returning it to the client. -TEST_F(SurfaceFactoryTest, ResourceReusedBeforeReturn) { - ResourceId first_frame_ids[] = {7}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // This removes all references to resource id 7. - SubmitCompositorFrameWithResources(NULL, 0); - - // This references id 7 again. - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // This removes it again. - SubmitCompositorFrameWithResources(NULL, 0); - - // Now it should be returned. - // We don't care how many entries are in the returned array for 7, so long as - // the total returned count matches the submitted count. - const ReturnedResourceArray& returned = - fake_surface_resource_holder_client_.returned_resources(); - size_t return_count = 0; - for (size_t i = 0; i < returned.size(); ++i) { - EXPECT_EQ(7u, returned[i].id); - return_count += returned[i].count; - } - EXPECT_EQ(2u, return_count); -} - -// Tests having resources referenced multiple times, as if referenced by -// multiple providers. -TEST_F(SurfaceFactoryTest, ResourceRefMultipleTimes) { - ResourceId first_frame_ids[] = {3, 4}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // Ref resources from the first frame twice. - RefCurrentFrameResources(); - RefCurrentFrameResources(); - - ResourceId second_frame_ids[] = {4, 5}; - SubmitCompositorFrameWithResources(second_frame_ids, - arraysize(second_frame_ids)); - - // Ref resources from the second frame 3 times. - RefCurrentFrameResources(); - RefCurrentFrameResources(); - RefCurrentFrameResources(); - - // Submit a frame with no resources to remove all current frame refs from - // submitted resources. - SubmitCompositorFrameWithResources(NULL, 0); - - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - // Expected current refs: - // 3 -> 2 - // 4 -> 2 + 3 = 5 - // 5 -> 3 - { - SCOPED_TRACE("unref all 3"); - ResourceId ids_to_unref[] = {3, 4, 5}; - int counts[] = {1, 1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {3}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - // Expected refs remaining: - // 4 -> 3 - // 5 -> 1 - { - SCOPED_TRACE("unref 4 and 5"); - ResourceId ids_to_unref[] = {4, 5}; - int counts[] = {1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {5}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - // Now, just 2 refs remaining on resource 4. Unref both at once and make sure - // the returned count is correct. - { - SCOPED_TRACE("unref only 4"); - ResourceId ids_to_unref[] = {4}; - int counts[] = {2}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {4}; - int expected_returned_counts[] = {2}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } -} - -TEST_F(SurfaceFactoryTest, ResourceLifetime) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - fake_surface_resource_holder_client_.clear_returned_resources(); - - // The second frame references some of the same resources, but some different - // ones. We expect to receive back resource 1 with a count of 1 since it was - // only referenced by the first frame. - ResourceId second_frame_ids[] = {2, 3, 4}; - SubmitCompositorFrameWithResources(second_frame_ids, - arraysize(second_frame_ids)); - - { - SCOPED_TRACE("second frame"); - ResourceId expected_returned_ids[] = {1}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - } - - // The third frame references a disjoint set of resources, so we expect to - // receive back all resources from the first and second frames. Resource IDs 2 - // and 3 will have counts of 2, since they were used in both frames, and - // resource ID 4 will have a count of 1. - ResourceId third_frame_ids[] = {10, 11, 12, 13}; - SubmitCompositorFrameWithResources(third_frame_ids, - arraysize(third_frame_ids)); - - { - SCOPED_TRACE("third frame"); - ResourceId expected_returned_ids[] = {2, 3, 4}; - int expected_returned_counts[] = {2, 2, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - } - - // Simulate a ResourceProvider taking a ref on all of the resources. - RefCurrentFrameResources(); - - ResourceId fourth_frame_ids[] = {12, 13}; - SubmitCompositorFrameWithResources(fourth_frame_ids, - arraysize(fourth_frame_ids)); - - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - - RefCurrentFrameResources(); - - // All resources are still being used by the external reference, so none can - // be returned to the client. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - - // Release resources associated with the first RefCurrentFrameResources() call - // first. - { - ResourceId ids_to_unref[] = {10, 11, 12, 13}; - int counts[] = {1, 1, 1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - } - - { - SCOPED_TRACE("fourth frame, first unref"); - ResourceId expected_returned_ids[] = {10, 11}; - int expected_returned_counts[] = {1, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - { - ResourceId ids_to_unref[] = {12, 13}; - int counts[] = {1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - } - - // Resources 12 and 13 are still in use by the current frame, so they - // shouldn't be available to be returned. - EXPECT_EQ(0u, - fake_surface_resource_holder_client_.returned_resources().size()); - - // If we submit an empty frame, however, they should become available. - SubmitCompositorFrameWithResources(NULL, 0u); - - { - SCOPED_TRACE("fourth frame, second unref"); - ResourceId expected_returned_ids[] = {12, 13}; - int expected_returned_counts[] = {2, 2}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } -} - -void CreateSurfaceDrawCallback(SurfaceFactory* factory, - uint32_t* execute_count) { - LocalSurfaceId new_id(7, base::UnguessableToken::Create()); - factory->SubmitCompositorFrame(new_id, test::MakeCompositorFrame(), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - factory->EvictSurface(); - *execute_count += 1; -} - -TEST_F(SurfaceFactoryTest, AddDuringEviction) { - LocalSurfaceId local_surface_id(6, kArbitraryToken); - - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame( - local_surface_id, test::MakeCompositorFrame(), - base::Bind(&CreateSurfaceDrawCallback, base::Unretained(factory_.get()), - &execute_count), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(0u, execute_count); - factory_->EvictSurface(); - EXPECT_EQ(1u, execute_count); -} - -void DrawCallback(uint32_t* execute_count) { - *execute_count += 1; -} - -// Tests doing an EvictSurface before shutting down the factory. -TEST_F(SurfaceFactoryTest, EvictSurface) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - SurfaceId id(kArbitraryFrameSinkId, local_surface_id); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame = test::MakeCompositorFrame(); - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - EXPECT_TRUE(manager_.GetSurfaceForId(id)); - EXPECT_TRUE( - fake_surface_resource_holder_client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_FALSE(manager_.GetSurfaceForId(id)); - EXPECT_FALSE( - fake_surface_resource_holder_client_.returned_resources().empty()); - EXPECT_EQ(1u, execute_count); -} - -// Tests doing an EvictSurface which has unregistered dependency. -TEST_F(SurfaceFactoryTest, EvictSurfaceDependencyUnRegistered) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame = test::MakeCompositorFrame(); - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - Surface* surface = manager_.GetSurfaceForId(surface_id); - surface->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE( - fake_surface_resource_holder_client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); - EXPECT_FALSE( - fake_surface_resource_holder_client_.returned_resources().empty()); - EXPECT_EQ(1u, execute_count); -} - -// Tests doing an EvictSurface which has registered dependency. -TEST_F(SurfaceFactoryTest, EvictSurfaceDependencyRegistered) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame = test::MakeCompositorFrame(); - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); - - SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - Surface* surface = manager_.GetSurfaceForId(surface_id); - surface->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE( - fake_surface_resource_holder_client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE( - fake_surface_resource_holder_client_.returned_resources().empty()); - EXPECT_EQ(0u, execute_count); - - manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); - EXPECT_FALSE( - fake_surface_resource_holder_client_.returned_resources().empty()); -} - -TEST_F(SurfaceFactoryTest, DestroySequence) { - LocalSurfaceId local_surface_id2(5, kArbitraryToken); - std::unique_ptr<SurfaceFactory> factory2(new SurfaceFactory( - kArbitraryFrameSinkId, &manager_, &stub_surface_factory_client_, - &fake_surface_resource_holder_client_)); - SurfaceId id2(kArbitraryFrameSinkId, local_surface_id2); - factory2->SubmitCompositorFrame( - local_surface_id2, test::MakeCompositorFrame(), - SurfaceFactory::DrawCallback(), SurfaceFactory::WillDrawCallback()); - - manager_.RegisterFrameSinkId(kArbitraryFrameSinkId); - - // Check that waiting before the sequence is satisfied works. - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kArbitraryFrameSinkId, 4)); - factory2->EvictSurface(); - - DCHECK(manager_.GetSurfaceForId(id2)); - manager_.SatisfySequence(SurfaceSequence(kArbitraryFrameSinkId, 4)); - manager_.SatisfySequence(SurfaceSequence(kArbitraryFrameSinkId, 6)); - DCHECK(!manager_.GetSurfaceForId(id2)); - - // Check that waiting after the sequence is satisfied works. - factory2->SubmitCompositorFrame( - local_surface_id2, test::MakeCompositorFrame(), - SurfaceFactory::DrawCallback(), SurfaceFactory::WillDrawCallback()); - DCHECK(manager_.GetSurfaceForId(id2)); - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 6)); - factory2->EvictSurface(); - DCHECK(!manager_.GetSurfaceForId(id2)); -} - -// Tests that Surface ID namespace invalidation correctly allows -// Sequences to be ignored. -TEST_F(SurfaceFactoryTest, InvalidFrameSinkId) { - FrameSinkId frame_sink_id(1234, 5678); - - LocalSurfaceId local_surface_id(5, kArbitraryToken); - SurfaceId id(factory_->frame_sink_id(), local_surface_id); - factory_->SubmitCompositorFrame(local_surface_id, test::MakeCompositorFrame(), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - - manager_.RegisterFrameSinkId(frame_sink_id); - manager_.GetSurfaceForId(id)->AddDestructionDependency( - SurfaceSequence(frame_sink_id, 4)); - - factory_->EvictSurface(); - - // Verify the dependency has prevented the surface from getting destroyed. - EXPECT_TRUE(manager_.GetSurfaceForId(id)); - - manager_.InvalidateFrameSinkId(frame_sink_id); - - // Verify that the invalidated namespace caused the unsatisfied sequence - // to be ignored. - EXPECT_FALSE(manager_.GetSurfaceForId(id)); -} - -TEST_F(SurfaceFactoryTest, DestroyCycle) { - LocalSurfaceId local_surface_id2(5, kArbitraryToken); - SurfaceId id2(kArbitraryFrameSinkId, local_surface_id2); - std::unique_ptr<SurfaceFactory> factory2(new SurfaceFactory( - kArbitraryFrameSinkId, &manager_, &stub_surface_factory_client_, - &fake_surface_resource_holder_client_)); - manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); - // Give id2 a frame that references local_surface_id_. - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame = test::MakeCompositorFrame(); - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory2->SubmitCompositorFrame(local_surface_id2, std::move(frame), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id2); - } - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - factory2->EvictSurface(); - // Give local_surface_id_ a frame that references id2. - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame = test::MakeCompositorFrame(); - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back(id2); - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - } - factory_->EvictSurface(); - EXPECT_TRUE(manager_.GetSurfaceForId(id2)); - // local_surface_id_ should be retained by reference from id2. - EXPECT_TRUE(manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_))); - - // Satisfy last destruction dependency for id2. - manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - // id2 and local_surface_id_ are in a reference cycle that has no surface - // sequences holding on to it, so they should be destroyed. - EXPECT_TRUE(!manager_.GetSurfaceForId(id2)); - EXPECT_TRUE(!manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_))); - - local_surface_id_ = LocalSurfaceId(); -} - -void CopyRequestTestCallback(bool* called, - std::unique_ptr<CopyOutputResult> result) { - *called = true; -} - -TEST_F(SurfaceFactoryTest, DuplicateCopyRequest) { - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame = test::MakeCompositorFrame(); - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id_); - } - - bool called1 = false; - std::unique_ptr<CopyOutputRequest> request; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called1)); - request->set_source(kArbitrarySourceId1); - - factory_->RequestCopyOfSurface(std::move(request)); - EXPECT_FALSE(called1); - - bool called2 = false; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called2)); - request->set_source(kArbitrarySourceId2); - - factory_->RequestCopyOfSurface(std::move(request)); - // Callbacks have different sources so neither should be called. - EXPECT_FALSE(called1); - EXPECT_FALSE(called2); - - bool called3 = false; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called3)); - request->set_source(kArbitrarySourceId1); - - factory_->RequestCopyOfSurface(std::move(request)); - // Two callbacks are from source1, so the first should be called. - EXPECT_TRUE(called1); - EXPECT_FALSE(called2); - EXPECT_FALSE(called3); - - factory_->EvictSurface(); - local_surface_id_ = LocalSurfaceId(); - EXPECT_TRUE(called1); - EXPECT_TRUE(called2); - EXPECT_TRUE(called3); -} - -// Check whether the SurfaceInfo object is created and populated correctly -// after the frame submission. -TEST_F(SurfaceFactoryTest, SurfaceInfo) { - CompositorFrame frame = test::MakeEmptyCompositorFrame(); - - auto render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(), gfx::Transform()); - frame.render_pass_list.push_back(std::move(render_pass)); - - render_pass = RenderPass::Create(); - render_pass->SetNew(2, gfx::Rect(7, 8), gfx::Rect(), gfx::Transform()); - frame.render_pass_list.push_back(std::move(render_pass)); - - frame.metadata.device_scale_factor = 2.5f; - - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback(), - SurfaceFactory::WillDrawCallback()); - SurfaceId expected_surface_id(factory_->frame_sink_id(), local_surface_id_); - EXPECT_EQ(expected_surface_id, last_surface_info_.id()); - EXPECT_EQ(2.5f, last_surface_info_.device_scale_factor()); - EXPECT_EQ(gfx::Size(7, 8), last_surface_info_.size_in_pixels()); -} - -} // namespace -} // namespace cc
diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc index e4267b4..19ea700 100644 --- a/cc/surfaces/surface_manager.cc +++ b/cc/surfaces/surface_manager.cc
@@ -11,10 +11,10 @@ #include <utility> #include "base/logging.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/direct_surface_reference_factory.h" #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_info.h" #if DCHECK_IS_ON() @@ -75,18 +75,20 @@ } std::unique_ptr<Surface> SurfaceManager::CreateSurface( - base::WeakPtr<SurfaceFactory> surface_factory, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, const LocalSurfaceId& local_surface_id) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(local_surface_id.is_valid() && surface_factory); + DCHECK(local_surface_id.is_valid() && compositor_frame_sink_support); - SurfaceId surface_id(surface_factory->frame_sink_id(), local_surface_id); + SurfaceId surface_id(compositor_frame_sink_support->frame_sink_id(), + local_surface_id); // If no surface with this SurfaceId exists, simply create the surface and // return. auto surface_iter = surface_map_.find(surface_id); if (surface_iter == surface_map_.end()) { - auto surface = base::MakeUnique<Surface>(surface_id, surface_factory); + auto surface = + base::MakeUnique<Surface>(surface_id, compositor_frame_sink_support); surface_map_[surface->surface_id()] = surface.get(); return surface; } @@ -108,7 +110,8 @@ std::unique_ptr<Surface> surface = std::move(*it); surfaces_to_destroy_.erase(it); surface->set_destroyed(false); - DCHECK_EQ(surface_factory.get(), surface->factory().get()); + DCHECK_EQ(compositor_frame_sink_support.get(), + surface->compositor_frame_sink_support().get()); return surface; }
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h index 91d9d9f..db716bd 100644 --- a/cc/surfaces/surface_manager.h +++ b/cc/surfaces/surface_manager.h
@@ -38,9 +38,10 @@ class CompositorFrame; class FrameSinkManagerClient; class Surface; -class SurfaceFactory; -class SurfaceFactoryClient; -class CompositorFrameSinkSupportTest; + +namespace test { +class SurfaceSynchronizationTest; +} class CC_SURFACES_EXPORT SurfaceManager { public: @@ -66,7 +67,7 @@ void RequestSurfaceResolution(Surface* pending_surface); std::unique_ptr<Surface> CreateSurface( - base::WeakPtr<SurfaceFactory> surface_factory, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, const LocalSurfaceId& local_surface_id); // Destroy the Surface once a set of sequence numbers has been satisfied. @@ -82,8 +83,8 @@ bool SurfaceModified(const SurfaceId& surface_id); - // Called when a CompositorFrame is submitted to a SurfaceFactory for a given - // |surface_id| for the first time. + // Called when a CompositorFrame is submitted to a CompositorFrameSinkSupport + // for a given |surface_id| for the first time. void SurfaceCreated(const SurfaceInfo& surface_info); // Require that the given sequence number must be satisfied (using @@ -101,8 +102,8 @@ // possibly because a renderer process has crashed. void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id); - // SurfaceFactoryClient, hierarchy, and BeginFrameSource can be registered - // and unregistered in any order with respect to each other. + // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be + // registered and unregistered in any order with respect to each other. // // This happens in practice, e.g. the relationship to between ui::Compositor / // DelegatedFrameHost is known before ui::Compositor has a surface/client). @@ -169,7 +170,7 @@ } private: - friend class CompositorFrameSinkSupportTest; + friend class test::SurfaceSynchronizationTest; friend class SurfaceManagerRefTest; using SurfaceIdSet = std::unordered_set<SurfaceId, SurfaceIdHash>;
diff --git a/cc/surfaces/surface_manager_unittest.cc b/cc/surfaces/surface_manager_unittest.cc index 53fe6ed9..07047fd 100644 --- a/cc/surfaces/surface_manager_unittest.cc +++ b/cc/surfaces/surface_manager_unittest.cc
@@ -6,9 +6,7 @@ #include "cc/scheduler/begin_frame_source.h" #include "cc/surfaces/frame_sink_manager_client.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_manager.h" -#include "cc/surfaces/surface_resource_holder_client.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -184,7 +182,7 @@ // This test verifies that a BeginFrameSource path to the root from a // FrameSinkId is preserved even if that FrameSinkId has no children -// and does not have a corresponding SurfaceFactoryClient. +// and does not have a corresponding FrameSinkManagerClient. TEST_F(SurfaceManagerTest, ParentWithoutClientRetained) { StubBeginFrameSource root_source; @@ -201,7 +199,7 @@ EXPECT_EQ(&root_source, root.source()); // Set up initial hierarchy: root -> A -> B. - // Note that A does not have a SurfaceFactoryClient. + // Note that A does not have a FrameSinkManagerClient. manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA); manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB); // The root's BeginFrameSource should propagate to B. @@ -237,7 +235,7 @@ FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_); // Set up initial hierarchy: root -> A -> B. - // Note that A does not have a SurfaceFactoryClient. + // Note that A does not have a FrameSinkManagerClient. manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA); manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB); // The root does not yet have a BeginFrameSource so client B should not have @@ -260,7 +258,7 @@ } // In practice, registering and unregistering both parent/child relationships -// and SurfaceFactoryClients can happen in any ordering with respect to +// and FrameSinkManagerClients can happen in any ordering with respect to // each other. These following tests verify that all the data structures // are properly set up and cleaned up under the four permutations of orderings // of this nesting.
diff --git a/cc/surfaces/surface_resource_holder.cc b/cc/surfaces/surface_resource_holder.cc index dff955c..8d46b25 100644 --- a/cc/surfaces/surface_resource_holder.cc +++ b/cc/surfaces/surface_resource_holder.cc
@@ -11,12 +11,10 @@ SurfaceResourceHolderClient* client) : client_(client) {} -SurfaceResourceHolder::~SurfaceResourceHolder() { -} +SurfaceResourceHolder::~SurfaceResourceHolder() = default; SurfaceResourceHolder::ResourceRefs::ResourceRefs() - : refs_received_from_child(0), refs_holding_resource_alive(0) { -} + : refs_received_from_child(0), refs_holding_resource_alive(0) {} void SurfaceResourceHolder::Reset() { resource_id_info_map_.clear(); @@ -24,10 +22,8 @@ void SurfaceResourceHolder::ReceiveFromChild( const TransferableResourceArray& resources) { - for (TransferableResourceArray::const_iterator it = resources.begin(); - it != resources.end(); - ++it) { - ResourceRefs& ref = resource_id_info_map_[it->id]; + for (const auto& resource : resources) { + ResourceRefs& ref = resource_id_info_map_[resource.id]; ref.refs_holding_resource_alive++; ref.refs_received_from_child++; }
diff --git a/cc/surfaces/surface_synchronization_unittest.cc b/cc/surfaces/surface_synchronization_unittest.cc new file mode 100644 index 0000000..8c21486 --- /dev/null +++ b/cc/surfaces/surface_synchronization_unittest.cc
@@ -0,0 +1,1389 @@ +// Copyright 2017 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 "cc/surfaces/compositor_frame_sink_support.h" +#include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_manager.h" +#include "cc/surfaces/surface_observer.h" +#include "cc/test/begin_frame_args_test.h" +#include "cc/test/compositor_frame_helpers.h" +#include "cc/test/fake_external_begin_frame_source.h" +#include "cc/test/mock_compositor_frame_sink_support_client.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Eq; +using testing::IsEmpty; +using testing::UnorderedElementsAre; + +namespace cc { +namespace test { +namespace { + +constexpr bool kIsRoot = true; +constexpr bool kIsChildRoot = false; +constexpr bool kHandlesFrameSinkIdInvalidation = true; +constexpr bool kNeedsSyncPoints = true; +constexpr FrameSinkId kDisplayFrameSink(2, 0); +constexpr FrameSinkId kParentFrameSink(3, 0); +constexpr FrameSinkId kChildFrameSink1(65563, 0); +constexpr FrameSinkId kChildFrameSink2(65564, 0); +constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); + +std::vector<SurfaceId> empty_surface_ids() { + return std::vector<SurfaceId>(); +} + +SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) { + return SurfaceId( + frame_sink_id, + LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); +} + +} // namespace + +class SurfaceSynchronizationTest : public testing::Test, + public SurfaceObserver { + public: + SurfaceSynchronizationTest() + : surface_manager_(SurfaceManager::LifetimeType::REFERENCES) {} + ~SurfaceSynchronizationTest() override {} + + CompositorFrameSinkSupport& display_support() { return *supports_[0]; } + Surface* display_surface() { + return display_support().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } + Surface* parent_surface() { + return parent_support().current_surface_for_testing(); + } + const ReferencedSurfaceTracker& parent_reference_tracker() { + return parent_support().ReferenceTrackerForTesting(); + } + + CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } + Surface* child_surface1() { + return child_support1().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } + Surface* child_surface2() { + return child_support2().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } + Surface* surface(int index) { + return support(index).current_surface_for_testing(); + } + + SurfaceManager& surface_manager() { return surface_manager_; } + + // Returns all the references where |surface_id| is the parent. + const SurfaceManager::SurfaceIdSet& GetChildReferences( + const SurfaceId& surface_id) { + return surface_manager().parent_to_child_refs_[surface_id]; + } + + // Returns true if there is a temporary reference for |surface_id|. + bool HasTemporaryReference(const SurfaceId& surface_id) { + return surface_manager().HasTemporaryReference(surface_id); + } + + SurfaceDependencyTracker& dependency_tracker() { + return *surface_manager_.dependency_tracker(); + } + + FakeExternalBeginFrameSource* begin_frame_source() { + return begin_frame_source_.get(); + } + + // testing::Test: + void SetUp() override { + testing::Test::SetUp(); + + begin_frame_source_ = + base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); + surface_manager_.SetDependencyTracker( + base::MakeUnique<SurfaceDependencyTracker>(&surface_manager_, + begin_frame_source_.get())); + surface_manager_.AddObserver(this); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kDisplayFrameSink, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kParentFrameSink, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kChildFrameSink1, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kChildFrameSink2, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + + // Normally, the BeginFrameSource would be registered by the Display. We + // register it here so that BeginFrames are received by the display support, + // for use in the PassesOnBeginFrameAcks test. Other supports do not receive + // BeginFrames, since the frame sink hierarchy is not set up in this test. + surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), + kDisplayFrameSink); + } + + void TearDown() override { + surface_manager_.RemoveObserver(this); + surface_manager_.SetDependencyTracker(nullptr); + surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); + + // SurfaceDependencyTracker depends on this BeginFrameSource and so it must + // be destroyed AFTER the dependency tracker is destroyed. + begin_frame_source_.reset(); + + supports_.clear(); + + damaged_surfaces_.clear(); + } + + bool IsSurfaceDamaged(const SurfaceId& surface_id) const { + return damaged_surfaces_.count(surface_id) > 0; + } + + // SurfaceObserver implementation: + void OnSurfaceCreated(const SurfaceInfo& surface_info) override {} + void OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) override { + damaged_surfaces_.insert(surface_id); + } + + protected: + testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; + + private: + base::flat_set<SurfaceId> damaged_surfaces_; + SurfaceManager surface_manager_; + std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; + std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceSynchronizationTest); +}; + +// The display root surface should have a surface reference from the top-level +// root added/removed when a CompositorFrame is submitted with a new SurfaceId. +TEST_F(SurfaceSynchronizationTest, RootSurfaceReceivesReferences) { + const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); + + // Submit a CompositorFrame for the first display root surface. + display_support().SubmitCompositorFrame(display_id_first.local_surface_id(), + MakeCompositorFrame()); + + // A surface reference from the top-level root is added and there shouldn't be + // a temporary reference. + EXPECT_FALSE(HasTemporaryReference(display_id_first)); + EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), + UnorderedElementsAre(display_id_first)); + + // Submit a CompositorFrame for the second display root surface. + display_support().SubmitCompositorFrame(display_id_second.local_surface_id(), + MakeCompositorFrame()); + + // A surface reference from the top-level root to |display_id_second| should + // be added and the reference to |display_root_first| removed. + EXPECT_FALSE(HasTemporaryReference(display_id_second)); + EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), + UnorderedElementsAre(display_id_second)); + + // Surface |display_id_first| is unreachable and should get deleted. + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first)); +} + +// The parent Surface is blocked on |child_id1| and |child_id2|. +TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1| and |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1, child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id1|. + // parent_support should now only be blocked on |child_id2|. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. +TEST_F(SurfaceSynchronizationTest, BlockedChain) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + // The parent should not report damage until it activates. + EXPECT_FALSE(IsSurfaceDamaged(parent_id)); + + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // child_support1 should now be blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + // The parent and child should not report damage until they activate. + EXPECT_FALSE(IsSurfaceDamaged(parent_id)); + EXPECT_FALSE(IsSurfaceDamaged(child_id1)); + + // The parent should still be blocked on |child_id1| because it's pending. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // child_surface1 should now be active. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // parent_surface should now be active. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // All three surfaces |parent_id|, |child_id1|, and |child_id2| should + // now report damage. This would trigger a new display frame. + EXPECT_TRUE(IsSurfaceDamaged(parent_id)); + EXPECT_TRUE(IsSurfaceDamaged(child_id1)); + EXPECT_TRUE(IsSurfaceDamaged(child_id2)); +} + +// parent_surface and child_surface1 are blocked on |child_id2|. +TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // child_support1 should now be blocked on |child_id2|. + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // The parent should still be blocked on |child_id2|. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // child_surface1 should now be active. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // parent_surface should now be active. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// parent_surface is blocked on |child_id1|, and child_surface2 is blocked on +// |child_id2| until the deadline hits. +TEST_F(SurfaceSynchronizationTest, DeadlineHits) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // child_support1 should now be blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // The parent should still be blocked on |child_id1| because it's pending. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + // There is still a looming deadline! Eeek! + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // parent_support is still blocked on |child_id1|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // child_support1 is still blocked on |child_id2|. + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + } + + begin_frame_source()->TestOnBeginFrame(args); + + // The deadline has passed. + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // parent_surface has been activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // child_surface1 has been activated. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); +} + +// Verifies that the deadline does not reset if we submit CompositorFrames +// to new Surfaces with unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, FramesSubmittedAfterDeadlineSet) { + const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); + support(i).SubmitCompositorFrame( + local_surface_id, + MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), + TransferableResourceArray())); + // The deadline has been set. + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // support(i) should be blocked on arbitrary_id. + EXPECT_FALSE(surface(i)->HasActiveFrame()); + EXPECT_TRUE(surface(i)->HasPendingFrame()); + EXPECT_THAT(surface(i)->blocking_surfaces(), + UnorderedElementsAre(arbitrary_id)); + + // Issue a BeginFrame to get closer to the deadline. + begin_frame_source()->TestOnBeginFrame(args); + } + + // The deadline hits and all the Surfaces should activate. + begin_frame_source()->TestOnBeginFrame(args); + for (int i = 0; i < 3; ++i) { + EXPECT_TRUE(surface(i)->HasActiveFrame()); + EXPECT_FALSE(surface(i)->HasPendingFrame()); + EXPECT_THAT(surface(i)->blocking_surfaces(), IsEmpty()); + } +} + +// This test verifies at the Surface activates once a CompositorFrame is +// submitted that has no unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); + + // Submit a CompositorFrame that depends on |arbitrary_id|. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |arbitrary_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(arbitrary_id)); + + // Submit a CompositorFrame that has no dependencies. + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the CompositorFrame has been activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// This test verifies that a pending CompositorFrame does not affect surface +// references. A new surface from a child will continue to exist as a temporary +// reference until the parent's frame activates. +TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // child_support1 submits a CompositorFrame without any dependencies. + // DidReceiveCompositorFrameAck should call on immediate activation. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(1); + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child surface is not blocked. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that there's a temporary reference for |child_id1|. + EXPECT_TRUE(HasTemporaryReference(child_id1)); + + // parent_support submits a CompositorFrame that depends on |child_id1| + // (which is already active) and |child_id2|. Thus, the parent should not + // activate immediately. DidReceiveCompositorFrameAck should not be called + // immediately because the parent CompositorFrame is also blocked on + // |child_id2|. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, {child_id1}, + TransferableResourceArray())); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that there's a temporary reference for |child_id1| that still + // exists. + EXPECT_TRUE(HasTemporaryReference(child_id1)); + + // child_support2 submits a CompositorFrame without any dependencies. + // Both the child and the parent should immediately ACK CompositorFrames + // on activation. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child surface is not blocked. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent surface's CompositorFrame has activated and that the + // temporary reference has been replaced by a permanent one. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + EXPECT_FALSE(HasTemporaryReference(child_id1)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); +} + +// This test verifies that we do not double count returned resources when a +// CompositorFrame starts out as pending, then becomes active, and then is +// replaced with another active CompositorFrame. +TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + // The parent submits a CompositorFrame that depends on |child_id| before the + // child submits a CompositorFrame. The CompositorFrame also has resources in + // its resource list. + TransferableResource resource; + resource.id = 1337; + resource.format = ALPHA_8; + resource.filter = 1234; + resource.size = gfx::Size(1234, 5678); + TransferableResourceArray resource_list = {resource}; + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), resource_list)); + + // Verify that the CompositorFrame is blocked on |child_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id)); + + child_support1().SubmitCompositorFrame( + child_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the child CompositorFrame activates immediately. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + ReturnedResourceArray returned_resources = {resource.ToReturnedResource()}; + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(returned_resources)); + + // The parent submits a CompositorFrame without any dependencies. That frame + // should activate immediately, replacing the earlier frame. The resource from + // the earlier frame should be returned to the client. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ids()}, + TransferableResourceArray())); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. +// child_support1 evicts its blocked Surface. The parent surface should +// activate. +TEST_F(SurfaceSynchronizationTest, EvictSurfaceWithPendingFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // Submit a CompositorFrame that depends on |child_id1|. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id1|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // Submit a CompositorFrame that depends on |child_id2|. + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id2|. + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Evict child_support1's current Surface. + // TODO(fsamuel): EvictFrame => EvictCurrentSurface. + child_support1().EvictFrame(); + + // The parent Surface should immediately activate. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + EXPECT_FALSE(dependency_tracker().has_deadline()); +} + +// This test verifies that if a surface has both a pending and active +// CompositorFrame and the pending CompositorFrame activates, replacing the +// existing active CompositorFrame, then the surface reference hierarchy will be +// updated allowing garbage collection of surfaces that are no longer +// referenced. +TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // The parent submits a CompositorFrame that depends on |child_id1| before the + // child submits a CompositorFrame. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that no references are added while the CompositorFrame is pending. + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); + + // DidReceiveCompositorFrameAck should get called twice: once for the child + // and once for the now active parent CompositorFrame. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child CompositorFrame activates immediately. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent Surface has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // Submit a new parent CompositorFrame to add a reference. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), {child_id1}, + TransferableResourceArray())); + + // Verify that the parent Surface has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // Verify that there is no temporary reference for the child and that + // the reference from the parent to the child still exists. + EXPECT_FALSE(HasTemporaryReference(child_id1)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + + // The parent submits another CompositorFrame that depends on |child_id2|. + // Submitting a pending CompositorFrame will not trigger a CompositorFrameAck. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // The parent surface should now have both a pending and activate + // CompositorFrame. Verify that the set of child references from + // |parent_id| are only from the active CompositorFrame. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the parent Surface has activated and no longer has a pending + // CompositorFrame. Also verify that |child_id1| is no longer a child + // reference of |parent_id|. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + // The parent will not immediately refer to the child until it submits a new + // CompositorFrame with the reference. + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. No frame has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with latency info + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Verify that the old surface has an active frame and no pending frame. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_FALSE(old_surface->HasPendingFrame()); + + // Submit another frame with some other latency info and a different + // LocalSurfaceId. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame(); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + std::move(frame2)); + + // Verify that the new surface has an active frame and no pending frames. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasActiveFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + + // Verify that the new surface has both latency info elements. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. Old surface has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with no unresolved dependecy. + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Submit a frame with unresolved dependencies. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray()); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame2)); + + // Verify that the old surface has both an active and a pending frame. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_TRUE(old_surface->HasPendingFrame()); + + // Submit a frame with a new local surface id. + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the new surface has an active frame only. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasActiveFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + + // Verify that the new surface has latency info from both active and pending + // frame of the old surface. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. The new surface has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with no unresolved dependencies. + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Verify that the old surface has an active frame only. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_FALSE(old_surface->HasPendingFrame()); + + // Submit a frame with a new local surface id and with unresolved + // dependencies. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray()); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + std::move(frame2)); + + // Verify that the new surface has a pending frame and no active frame. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasPendingFrame()); + EXPECT_FALSE(surface->HasActiveFrame()); + + // Resolve the dependencies. The frame in parent's surface must become active. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + EXPECT_TRUE(surface->HasActiveFrame()); + + // Both latency info elements must exist in the now-activated frame of the + // new surface. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +TEST_F(SurfaceSynchronizationTest, PassesOnBeginFrameAcks) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + + // Request BeginFrames. + display_support().SetNeedsBeginFrame(true); + + // Issue a BeginFrame. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + begin_frame_source()->TestOnBeginFrame(args); + + // Check that the support forwards a BeginFrameDidNotSwap ack to the + // BeginFrameSource. + BeginFrameAck ack(0, 1, 1, false); + display_support().BeginFrameDidNotSwap(ack); + EXPECT_EQ(ack, begin_frame_source()->LastAckForObserver(&display_support())); + + // Issue another BeginFrame. + args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); + begin_frame_source()->TestOnBeginFrame(args); + + // Check that the support forwards the BeginFrameAck attached + // to a CompositorFrame to the BeginFrameSource. + BeginFrameAck ack2(0, 2, 2, true); + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.begin_frame_ack = ack2; + display_support().SubmitCompositorFrame(display_id.local_surface_id(), + std::move(frame)); + EXPECT_EQ(ack2, begin_frame_source()->LastAckForObserver(&display_support())); +} + +// Checks that resources and ack are sent together if possible. +TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + TransferableResource resource; + resource.id = 1234; + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource})); + ReturnedResourceArray returned_resources; + TransferableResource::ReturnResources({resource}, &returned_resources); + EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources))); + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); +} + +// Verifies that if a surface is marked destroyed and a new frame arrives for +// it, it will be recovered. +TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); + + // Create the child surface by submitting a frame to it. + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the child surface is created. + Surface* surface = surface_manager().GetSurfaceForId(child_id); + EXPECT_NE(nullptr, surface); + + // Add a reference from the parent to the child. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); + + // Attempt to destroy the child surface. The surface must still exist since + // the parent needs it but it will be marked as destroyed. + child_support1().EvictFrame(); + surface = surface_manager().GetSurfaceForId(child_id); + EXPECT_NE(nullptr, surface); + EXPECT_TRUE(surface->destroyed()); + + // Child submits another frame to the same local surface id that is marked + // destroyed. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the surface that was marked destroyed is recovered and is being + // used again. + Surface* surface2 = surface_manager().GetSurfaceForId(child_id); + EXPECT_EQ(surface, surface2); + EXPECT_FALSE(surface2->destroyed()); +} + +// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist +// anymore, it can still be reused for new surfaces. +TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); + + // Submit the first frame. Creates the surface. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); + + // Add a reference from parent. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); + + // Remove the reference from parant. This allows us to destroy the surface. + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); + + // Destroy the surface. + child_support1().EvictFrame(); + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); + + // Submit another frame with the same local surface id. This should work fine + // and a new surface must be created. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); +} + +// This test verifies that a crash does not occur if garbage collection is +// triggered during surface dependency resolution. This test triggers garbage +// collection during surface resolution, by causing an activation to remove +// a surface subtree from the root. Both the old subtree and the new +// activated subtree refer to the same dependency. The old subtree was activated +// by deadline, and the new subtree was activated by a dependency finally +// resolving. +TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + + // Advance BeginFrames to trigger a deadline. + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + + parent_support().SubmitCompositorFrame( + parent_id2.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // The display surface now has two CompositorFrames. One that is pending, + // indirectly blocked on child_id and one that is active, also indirectly + // referring to child_id, but activated due to the deadline above. + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(display_surface()->HasPendingFrame()); + + // Submitting a CompositorFrame will trigger garbage collection of the + // |parent_id1| subtree. This should not crash. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); +} + +// This test verifies that a crash does not occur if garbage collection is +// triggered when a deadline forces frame activation. This test triggers garbage +// collection during deadline activation by causing the activation of a display +// frame to replace a previously activated display frame that was referring to +// a now-unreachable surface subtree. That subtree gets garbage collected during +// deadline activation. SurfaceDependencyTracker is also tracking a surface +// from that subtree due to an unresolved dependency. This test verifies that +// this dependency resolution does not crash. +TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + // |parent_id1| is blocked on |child_id|. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, {parent_id1}, + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted above. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + + // By submitting a display CompositorFrame, and replacing the parent's + // CompositorFrame with another surface ID, parent_id1 becomes unreachable and + // a candidate for garbage collection. + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // Now |parent_id1| is only kept alive by the active |display_id| frame. + parent_support().SubmitCompositorFrame( + parent_id2.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + + // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| + // and |parent_id2|. By activating the pending |display_id| frame by deadline, + // |parent_id1| becomes unreachable and is garbage collected while + // SurfaceDependencyTracker is in the process of activating surfaces. This + // should not cause a crash or use-after-free. + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); +} + +// This test verifies that a CompositorFrame will only blocked on embedded +// surfaces but not on other retained surface IDs in the CompositorFrame. +TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + + // Submitting a CompositorFrame with |parent_id2| so that the display + // CompositorFrame can hold a reference to it. + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + MakeCompositorFrame()); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, {parent_id2}, + TransferableResourceArray())); + + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // Verify that the display CompositorFrame will only block on |parent_id1| but + // not |parent_id2|. + EXPECT_THAT(display_surface()->blocking_surfaces(), + UnorderedElementsAre(parent_id1)); + // Verify that the display surface holds no references while its + // CompositorFrame is pending. + EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); + + // Submitting a CompositorFrame with |parent_id1| should unblock the display + // CompositorFrame. + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty()); +} + +// This test verifies that a late arriving CompositorFrame activates immediately +// and does not trigger a new deadline. +TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted above. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + + // A late arriving CompositorFrame should activate immediately without + // scheduling a deadline and without waiting for dependencies to resolve. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); +} + +// This test verifies that CompositorFrames submitted to a surface referenced +// by a parent CompositorFrame as a fallback will be rejected and ACK'ed +// immediately. +TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + // This is the fallback child surface that the parent holds a reference to. + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + // This is the primary child surface that the parent wants to block on. + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); + + // child_support1 submits a CompositorFrame without any dependencies. + // DidReceiveCompositorFrameAck should call on immediate activation. + // However, resources will not be returned because this frame is a candidate + // for display. + TransferableResource resource; + resource.id = 1337; + resource.format = ALPHA_8; + resource.filter = 1234; + resource.size = gfx::Size(1234, 5678); + ReturnedResourceArray returned_resources; + TransferableResource::ReturnResources({resource}, &returned_resources); + + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(ReturnedResourceArray()))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource})); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // The parent is blocked on |child_id2| and references |child_id1|. The + // surface corresponding to |child_id1| will not accept new CompositorFrames + // while the parent CompositorFrame is blocked. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, {child_id1}, + TransferableResourceArray())); + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + + // Resources will be returned immediately because |child_id1|'s surface is + // closed. + TransferableResource resource2; + resource2.id = 1246; + resource2.format = ALPHA_8; + resource2.filter = 1357; + resource2.size = gfx::Size(8765, 4321); + ReturnedResourceArray returned_resources2; + TransferableResource::ReturnResources({resource2}, &returned_resources2); + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources2))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource2})); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted to the parent. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + + // Resources will be returned immediately because |child_id1|'s surface is + // closed forever. + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources2))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource2})); + testing::Mock::VerifyAndClearExpectations(&support_client_); +} + +} // namespace test +} // namespace cc
diff --git a/cc/test/compositor_frame_helpers.cc b/cc/test/compositor_frame_helpers.cc index 23cc993..33e0ed63 100644 --- a/cc/test/compositor_frame_helpers.cc +++ b/cc/test/compositor_frame_helpers.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "cc/test/compositor_frame_helpers.h" + #include "cc/output/compositor_frame.h" namespace cc { @@ -25,5 +26,19 @@ return frame; } +CompositorFrame MakeCompositorFrame( + std::vector<SurfaceId> activation_dependencies, + std::vector<SurfaceId> referenced_surfaces, + TransferableResourceArray resource_list) { + CompositorFrame compositor_frame = test::MakeCompositorFrame(); + compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true); + compositor_frame.metadata.activation_dependencies = + std::move(activation_dependencies); + compositor_frame.metadata.referenced_surfaces = + std::move(referenced_surfaces); + compositor_frame.resource_list = std::move(resource_list); + return compositor_frame; +} + } // namespace test } // namespace cc
diff --git a/cc/test/compositor_frame_helpers.h b/cc/test/compositor_frame_helpers.h index c60de21..32356e6 100644 --- a/cc/test/compositor_frame_helpers.h +++ b/cc/test/compositor_frame_helpers.h
@@ -5,6 +5,11 @@ #ifndef CC_TEST_COMPOSITOR_FRAME_HELPERS_H_ #define CC_TEST_COMPOSITOR_FRAME_HELPERS_H_ +#include <vector> + +#include "cc/resources/transferable_resource.h" +#include "cc/surfaces/surface_id.h" + namespace cc { class CompositorFrame; @@ -18,6 +23,11 @@ // initialized. CompositorFrame MakeEmptyCompositorFrame(); +CompositorFrame MakeCompositorFrame( + std::vector<SurfaceId> activation_dependencies, + std::vector<SurfaceId> referenced_surfaces, + TransferableResourceArray resource_list); + } // namespace test } // namespace cc
diff --git a/cc/test/mock_compositor_frame_sink_support_client.cc b/cc/test/mock_compositor_frame_sink_support_client.cc new file mode 100644 index 0000000..758c87d8 --- /dev/null +++ b/cc/test/mock_compositor_frame_sink_support_client.cc
@@ -0,0 +1,22 @@ +// Copyright 2017 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 "cc/test/mock_compositor_frame_sink_support_client.h" + +#include "cc/output/begin_frame_args.h" +#include "cc/surfaces/local_surface_id.h" +#include "ui/gfx/geometry/rect.h" + +namespace cc { +namespace test { + +MockCompositorFrameSinkSupportClient::MockCompositorFrameSinkSupportClient() = + default; + +MockCompositorFrameSinkSupportClient::~MockCompositorFrameSinkSupportClient() = + default; + +} // namespace test + +} // namespace cc
diff --git a/cc/test/mock_compositor_frame_sink_support_client.h b/cc/test/mock_compositor_frame_sink_support_client.h new file mode 100644 index 0000000..792bb9d --- /dev/null +++ b/cc/test/mock_compositor_frame_sink_support_client.h
@@ -0,0 +1,31 @@ +// Copyright 2017 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. + +#ifndef CC_TEST_MOCK_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_ +#define CC_TEST_MOCK_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_ + +#include "cc/surfaces/compositor_frame_sink_support_client.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace cc { +namespace test { + +class MockCompositorFrameSinkSupportClient + : public CompositorFrameSinkSupportClient { + public: + MockCompositorFrameSinkSupportClient(); + ~MockCompositorFrameSinkSupportClient() override; + + // CompositorFrameSinkSupportClient implementation. + MOCK_METHOD1(DidReceiveCompositorFrameAck, + void(const ReturnedResourceArray&)); + MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&)); + MOCK_METHOD1(ReclaimResources, void(const ReturnedResourceArray&)); + MOCK_METHOD2(WillDrawSurface, void(const LocalSurfaceId&, const gfx::Rect&)); +}; + +} // namespace test +} // namespace cc + +#endif // CC_TEST_MOCK_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 493eb02..7e193ca 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -167,6 +167,7 @@ ":chrome_public_android_manifest", ":chrome_public_apk_template_resources", ":document_tab_model_info_proto_java", + ":partner_location_descriptor_proto_java", "//base:base_java", "//chrome/android/webapk/libs/client:client_java", "//chrome/android/webapk/libs/common:common_java", @@ -321,6 +322,13 @@ ] } +proto_java_library("partner_location_descriptor_proto_java") { + proto_path = "java/src/org/chromium/chrome/browser/omnibox/geo" + sources = [ + "$proto_path/partner_location_descriptor.proto", + ] +} + java_cpp_template("resource_id_javagen") { sources = [ "java/ResourceId.template", @@ -423,6 +431,7 @@ } deps = [ + ":partner_location_descriptor_proto_java", "//base:base_java", "//base:base_java_test_support", "//chrome/android:app_hooks_java", @@ -474,6 +483,7 @@ "//third_party/WebKit/public:android_mojo_bindings_java", "//third_party/WebKit/public:blink_headers_java", "//third_party/WebKit/public:mojo_bindings_java", + "//third_party/android_protobuf:protobuf_nano_javalib", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", "//third_party/android_tools:android_support_design_java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index ffaaa4d..6afd8e2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -192,6 +192,7 @@ public static final String WEB_PAYMENTS_MODIFIERS = "WebPaymentsModifiers"; public static final String WEB_PAYMENTS_SINGLE_APP_UI_SKIP = "WebPaymentsSingleAppUiSkip"; public static final String WEBVR_CARDBOARD_SUPPORT = "WebVRCardboardSupport"; + public static final String XGEO_VISIBLE_NETWORKS = "XGEOVisibleNetworks"; private static native boolean nativeIsInitialized(); private static native boolean nativeIsEnabled(String featureName);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java index d403e34..aca80473 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
@@ -16,6 +16,8 @@ import android.support.annotation.IntDef; import android.util.Base64; +import com.google.protobuf.nano.MessageNano; + import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ContextUtils; import org.chromium.base.Log; @@ -183,6 +185,15 @@ /** The maximum age in milliseconds of a location before we'll request a refresh. */ private static final int REFRESH_LOCATION_AGE = 5 * 60 * 1000; // 5 minutes + /** The X-Geo header prefix, preceding any location descriptors */ + private static final String XGEO_HEADER_PREFIX = "X-Geo: "; + + /** The location descriptor prefix used in the X-Geo header to specify a proto wire encoding */ + private static final String LOCATION_PROTO_PREFIX = "w "; + + /** The location descriptor prefix used in the X-Geo header to specify an ASCII encoding */ + private static final String LOCATION_ASCII_PREFIX = "a "; + /** The time of the first location refresh. Contains Long.MAX_VALUE if not set. */ private static long sFirstLocationTime = Long.MAX_VALUE; @@ -301,9 +312,9 @@ // Timestamp in microseconds since the UNIX epoch. long timestamp = location.getTime() * 1000; // Latitude times 1e7. - int latitude = (int) (location.getLatitude() * 10000000); + int latitudeE7 = (int) (location.getLatitude() * 10000000); // Longitude times 1e7. - int longitude = (int) (location.getLongitude() * 10000000); + int longitudeE7 = (int) (location.getLongitude() * 10000000); // Radius of 68% accuracy in mm. int radius = (int) (location.getAccuracy() * 1000); @@ -311,10 +322,35 @@ // https://goto.google.com/partner_location_proto String locationAscii = String.format(Locale.US, "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d longitude_e7:%d} radius:%d", - timestamp, latitude, longitude, radius); - String locationBase64 = new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRAP)); + timestamp, latitudeE7, longitudeE7, radius); + String locationAsciiEncoding = + new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRAP)); - return "X-Geo: a " + locationBase64; + boolean isXGeoVisibleNetworksEnabled = + ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWORKS); + if (!isXGeoVisibleNetworksEnabled) { + return XGEO_HEADER_PREFIX + LOCATION_ASCII_PREFIX + locationAsciiEncoding; + } + + // Create a LatLng for the coordinates. + PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor.LatLng(); + latlng.latitudeE7 = latitudeE7; + latlng.longitudeE7 = longitudeE7; + + // Populate a LocationDescriptor with the LatLng. + PartnerLocationDescriptor.LocationDescriptor locationDescriptor = + new PartnerLocationDescriptor.LocationDescriptor(); + locationDescriptor.latlng = latlng; + // Include role, producer, timestamp and radius. + locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION; + locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION; + locationDescriptor.timestamp = timestamp; + locationDescriptor.radius = (float) radius; + + String locationProtoEncoding = Base64.encodeToString( + MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Base64.URL_SAFE); + + return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + locationProtoEncoding; } @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/partner_location_descriptor.proto b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/partner_location_descriptor.proto new file mode 100644 index 0000000..e1b36c4e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/partner_location_descriptor.proto
@@ -0,0 +1,81 @@ +// Copyright 2017 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. +// +// Protocol buffer definition for a location descriptor, used for sending +// geographical information to the Default Search Engine in omnibox queries +// when location permissions are granted. +// +// TODO(lbargu): Move over to standard Geo header once it is defined, and remove +// this proto structure from chromium code base. +// +// NOTE: This is currently using a Google-defined structure. This explains the +// jumps in values. New fields cannot be added to this proto directly. See +// https://goto.google.com/partner_location_proto + +syntax = "proto2"; + +package org.chromium.chrome.browser.omnibox.geo; + +option java_outer_classname = "PartnerLocationDescriptor"; +option java_package = "org.chromium.chrome.browser.omnibox.geo"; + +enum LocationRole { + UNKNOWN_ROLE = 0; + CURRENT_LOCATION = 1; +} + +enum LocationProducer { + UNKNOWN_PRODUCER = 0; + DEVICE_LOCATION = 12; +} + +message LatLng { + optional sfixed32 latitude_e7 = 1; + optional sfixed32 longitude_e7 = 2; +} + +message VisibleNetwork { + + message WiFi { + optional string bssid = 1; + optional int32 level_dbm = 2; + } + + message Cell { + + enum Type { + UNKNOWN = 0; + GSM = 1; + LTE = 2; + CDMA = 3; + WCDMA = 4; + } + + optional Type type = 1; + optional int32 cell_id = 2; + optional int32 location_area_code = 3; + optional int32 mobile_country_code = 4; + optional int32 mobile_network_code = 5; + optional int32 primary_scrambling_code = 6; + optional int32 physical_cell_id = 7; + optional int32 tracking_area_code = 8; + } + + oneof type { + WiFi wifi = 1; + Cell cell = 2; + } + + optional bool connected = 3; + optional int64 timestamp_ms = 4; +} + +message LocationDescriptor { + optional LocationRole role = 1 [default = UNKNOWN_ROLE]; + optional LocationProducer producer = 2 [default = UNKNOWN_PRODUCER]; + optional int64 timestamp = 3; + optional LatLng latlng = 5; + optional float radius = 7; + repeated VisibleNetwork visible_network = 23; +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java index a0ed7d3..090267f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
@@ -9,6 +9,9 @@ import android.os.Build; import android.os.SystemClock; import android.support.test.filters.SmallTest; +import android.util.Base64; + +import com.google.protobuf.nano.MessageNano; import org.chromium.base.ThreadUtils; import org.chromium.base.library_loader.ProcessInitException; @@ -22,6 +25,8 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.test.ChromeActivityTestCaseBase; +import java.util.Locale; + /** * Tests for GeolocationHeader and GeolocationTracker. */ @@ -32,7 +37,16 @@ "enable-features=ConsistentOmniboxGeolocation"; private static final String DISABLE_CONSISTENT_GEOLOCATION_FEATURE = "disable-features=ConsistentOmniboxGeolocation"; + private static final String ENABLE_XGEO_VISIBLE_NETWORKS = + "enable-features=XGEOVisibleNetworks"; + private static final String DISABLE_XGEO_VISIBLE_NETWORKS = + "disable-features=XGEOVisibleNetworks"; private static final String GOOGLE_BASE_URL_SWITCH = "google-base-url=https://www.google.com"; + private static final double LOCATION_LAT = 20.3; + private static final double LOCATION_LONG = 155.8; + private static final float LOCATION_ACCURACY = 20f; + private static final boolean ENCODING_PROTO = true; + private static final boolean ENCODING_ASCII = false; public GeolocationHeaderTest() { super(ChromeActivity.class); @@ -51,13 +65,13 @@ @SmallTest @Feature({"Location"}) - @CommandLineFlags.Add(DISABLE_CONSISTENT_GEOLOCATION_FEATURE) + @CommandLineFlags.Add({DISABLE_CONSISTENT_GEOLOCATION_FEATURE, DISABLE_XGEO_VISIBLE_NETWORKS}) public void testGeolocationHeader() throws ProcessInitException { - setMockLocation(20.3, 155.8, System.currentTimeMillis()); + long now = setMockLocationNow(); // X-Geo should be sent for Google search results page URLs. - assertNonNullHeader(SEARCH_URL_1, false); - assertNonNullHeader(SEARCH_URL_2, false); + assertNonNullHeader(SEARCH_URL_1, false, now, ENCODING_ASCII); + assertNonNullHeader(SEARCH_URL_2, false, now, ENCODING_ASCII); // X-Geo shouldn't be sent in incognito mode. assertNullHeader(SEARCH_URL_1, true); @@ -75,12 +89,13 @@ @SmallTest @Feature({"Location"}) - @CommandLineFlags.Add({ENABLE_CONSISTENT_GEOLOCATION_FEATURE, GOOGLE_BASE_URL_SWITCH}) + @CommandLineFlags.Add({ENABLE_CONSISTENT_GEOLOCATION_FEATURE, GOOGLE_BASE_URL_SWITCH, + DISABLE_XGEO_VISIBLE_NETWORKS}) public void testConsistentHeader() throws ProcessInitException { - setMockLocation(20.3, 155.8, System.currentTimeMillis()); + long now = setMockLocationNow(); // X-Geo should be sent for Google search results page URLs. - assertNonNullHeader(SEARCH_URL_1, false); + assertNonNullHeader(SEARCH_URL_1, false, now, ENCODING_ASCII); // But only the current CCTLD. assertNullHeader(SEARCH_URL_2, false); @@ -103,47 +118,68 @@ @Feature({"Location"}) @CommandLineFlags.Add(DISABLE_CONSISTENT_GEOLOCATION_FEATURE) public void testPermissions() throws ProcessInitException { - setMockLocation(20.3, 155.8, System.currentTimeMillis()); + long now = setMockLocationNow(); // X-Geo shouldn't be sent when location is disallowed for https origin. - checkHeaderWithPermissions(ContentSetting.ALLOW, false); - checkHeaderWithPermissions(ContentSetting.DEFAULT, false); - checkHeaderWithPermissions(ContentSetting.BLOCK, true); + checkHeaderWithPermissions(ContentSetting.ALLOW, now, false); + checkHeaderWithPermissions(ContentSetting.DEFAULT, now, false); + checkHeaderWithPermissions(ContentSetting.BLOCK, now, true); } @SmallTest @Feature({"Location"}) - @CommandLineFlags.Add({ENABLE_CONSISTENT_GEOLOCATION_FEATURE, GOOGLE_BASE_URL_SWITCH}) + @CommandLineFlags.Add({ENABLE_CONSISTENT_GEOLOCATION_FEATURE, GOOGLE_BASE_URL_SWITCH, + DISABLE_XGEO_VISIBLE_NETWORKS}) public void testPermissionAndSetting() throws ProcessInitException { - setMockLocation(20.3, 155.8, System.currentTimeMillis()); + long now = setMockLocationNow(); // X-Geo shouldn't be sent when location is disallowed for the origin, or when the DSE // geolocation setting is off. - checkHeaderWithPermissionAndSetting(ContentSetting.ALLOW, true, false); - checkHeaderWithPermissionAndSetting(ContentSetting.DEFAULT, true, false); - checkHeaderWithPermissionAndSetting(ContentSetting.DEFAULT, false, true); - checkHeaderWithPermissionAndSetting(ContentSetting.BLOCK, false, true); + checkHeaderWithPermissionAndSetting(ContentSetting.ALLOW, true, now, false); + checkHeaderWithPermissionAndSetting(ContentSetting.DEFAULT, true, now, false); + checkHeaderWithPermissionAndSetting(ContentSetting.DEFAULT, false, now, true); + checkHeaderWithPermissionAndSetting(ContentSetting.BLOCK, false, now, true); } @SmallTest @Feature({"Location"}) - @CommandLineFlags.Add(DISABLE_CONSISTENT_GEOLOCATION_FEATURE) + @CommandLineFlags.Add({DISABLE_CONSISTENT_GEOLOCATION_FEATURE, DISABLE_XGEO_VISIBLE_NETWORKS}) public void testOnlyNonStale() throws ProcessInitException { - setMockLocation(20.3, 155.8, System.currentTimeMillis()); - // X-Geo should be sent only with non-stale locations. long now = System.currentTimeMillis(); long oneHour = 60 * 60 * 1000; long oneWeek = 7 * 24 * 60 * 60 * 1000; - checkHeaderWithLocation(20.3, 155.8, now, false); - checkHeaderWithLocation(20.3, 155.8, now - oneHour, false); - checkHeaderWithLocation(20.3, 155.8, now - oneWeek, true); + setMockLocation(now); + + checkHeaderWithLocation(now, false); + checkHeaderWithLocation(now - oneHour, false); + checkHeaderWithLocation(now - oneWeek, true); GeolocationTracker.setLocationForTesting(null); assertNullHeader(SEARCH_URL_1, false); } - private void checkHeaderWithPermissions( - final ContentSetting httpsPermission, final boolean shouldBeNull) { + @SmallTest + @Feature({"Location"}) + @CommandLineFlags.Add({DISABLE_XGEO_VISIBLE_NETWORKS}) + public void testAsciiEncoding() throws ProcessInitException { + long now = setMockLocationNow(); + + // X-Geo should be sent for Google search results page URLs using ascii encoding. + assertNonNullHeader(SEARCH_URL_1, false, now, ENCODING_ASCII); + } + + @SmallTest + @Feature({"Location"}) + @CommandLineFlags.Add({ENABLE_XGEO_VISIBLE_NETWORKS}) + public void testProtoEncoding() throws ProcessInitException { + long now = setMockLocationNow(); + + // X-Geo should be sent for Google search results page URLs using proto encoding. + assertNonNullHeader(SEARCH_URL_1, false, now, ENCODING_PROTO); + } + + private void checkHeaderWithPermissions(final ContentSetting httpsPermission, + final long locationTime, final boolean shouldBeNull) { ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { @@ -153,13 +189,13 @@ String header = GeolocationHeader.getGeoHeader( "https://www.google.de/search?q=kartoffelsalat", getActivity().getActivityTab()); - assertHeaderState(header, shouldBeNull); + assertHeaderState(header, locationTime, shouldBeNull); } }); } private void checkHeaderWithPermissionAndSetting(final ContentSetting httpsPermission, - final boolean settingValue, final boolean shouldBeNull) { + final boolean settingValue, final long locationTime, final boolean shouldBeNull) { ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { @@ -169,37 +205,42 @@ WebsitePreferenceBridge.setDSEGeolocationSetting(settingValue); String header = GeolocationHeader.getGeoHeader( SEARCH_URL_1, getActivity().getActivityTab()); - assertHeaderState(header, shouldBeNull); + assertHeaderState(header, locationTime, shouldBeNull); } }); } - private void checkHeaderWithLocation(final double latitute, final double longitude, - final long time, final boolean shouldBeNull) { + private void checkHeaderWithLocation(final long locationTime, final boolean shouldBeNull) { ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - setMockLocation(latitute, longitude, time); + setMockLocation(locationTime); String header = GeolocationHeader.getGeoHeader(SEARCH_URL_1, getActivity().getActivityTab()); - assertHeaderState(header, shouldBeNull); + assertHeaderState(header, locationTime, shouldBeNull); } }); } - private void assertHeaderState(String header, boolean shouldBeNull) { + private void assertHeaderState(String header, long locationTime, boolean shouldBeNull) { if (shouldBeNull) { assertNull(header); } else { - assertNotNull(header); + assertHeaderEquals(locationTime, ENCODING_ASCII, header); } } - private void setMockLocation(double latitute, double longitude, long time) { + private long setMockLocationNow() { + long now = System.currentTimeMillis(); + setMockLocation(now); + return now; + } + + private void setMockLocation(long time) { final Location location = new Location(LocationManager.NETWORK_PROVIDER); - location.setLatitude(latitute); - location.setLongitude(longitude); - location.setAccuracy(20f); + location.setLatitude(LOCATION_LAT); + location.setLongitude(LOCATION_LONG); + location.setAccuracy(LOCATION_ACCURACY); location.setTime(time); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos() @@ -222,13 +263,15 @@ } } - private void assertNonNullHeader(final String url, final boolean isIncognito) { + private void assertNonNullHeader(final String url, final boolean isIncognito, + final long locationTime, final boolean encodingType) { try { final Tab tab = loadUrlInNewTab("about:blank", isIncognito); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - assertNotNull(GeolocationHeader.getGeoHeader(url, tab)); + assertHeaderEquals( + locationTime, encodingType, GeolocationHeader.getGeoHeader(url, tab)); } }); } catch (InterruptedException e) { @@ -236,6 +279,47 @@ } } + private void assertHeaderEquals(long locationTime, boolean encodingType, String header) { + long timestamp = locationTime * 1000; + // Latitude times 1e7. + int latitudeE7 = (int) (LOCATION_LAT * 10000000); + // Longitude times 1e7. + int longitudeE7 = (int) (LOCATION_LONG * 10000000); + // Radius of 68% accuracy in mm. + int radius = (int) (LOCATION_ACCURACY * 1000); + + String expectedHeader; + if (encodingType == ENCODING_PROTO) { + // Create a LatLng for the coordinates. + PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor.LatLng(); + latlng.latitudeE7 = latitudeE7; + latlng.longitudeE7 = longitudeE7; + + // Populate a LocationDescriptor with the LatLng. + PartnerLocationDescriptor.LocationDescriptor locationDescriptor = + new PartnerLocationDescriptor.LocationDescriptor(); + locationDescriptor.latlng = latlng; + // Include role, producer, timestamp and radius. + locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION; + locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION; + locationDescriptor.timestamp = timestamp; + locationDescriptor.radius = (float) radius; + + String locationProto = Base64.encodeToString( + MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Base64.URL_SAFE); + expectedHeader = "X-Geo: w " + locationProto; + } else { + assertEquals(ENCODING_ASCII, encodingType); + String locationAscii = String.format(Locale.US, + "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d longitude_e7:%d} " + + "radius:%d", + timestamp, latitudeE7, longitudeE7, radius); + expectedHeader = "X-Geo: a " + + new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRAP)); + } + assertEquals(expectedHeader, header); + } + @Override public void startMainActivity() throws InterruptedException { startMainActivityOnBlankPage();
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 16a1c17..cdaea04b 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2847,6 +2847,11 @@ flag_descriptions::kLocationHardReloadDescription, kOsAll, FEATURE_VALUE_TYPE(features::kLocationHardReload)}, + {"capture-thumbnail-on-load-finished", + flag_descriptions::kCaptureThumbnailOnLoadFinishedName, + flag_descriptions::kCaptureThumbnailOnLoadFinishedDescription, kOsDesktop, + FEATURE_VALUE_TYPE(features::kCaptureThumbnailOnLoadFinished)}, + // NOTE: Adding new command-line switches requires adding corresponding // entries to enum "LoginCustomFlags" in histograms/enums.xml. See note in // enums.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index 64abee2d..582f824 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -80,6 +80,7 @@ &kWebPaymentsModifiers, &kWebPaymentsSingleAppUiSkip, &kWebVRCardboardSupport, + &kXGEOVisibleNetworks, &ntp_snippets::kIncreasedVisibility, &ntp_snippets::kForeignSessionsSuggestionsFeature, &ntp_snippets::kNotificationsFeature, @@ -217,6 +218,9 @@ const base::Feature kWebVRCardboardSupport{ "WebVRCardboardSupport", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kXGEOVisibleNetworks{"XGEOVisibleNetworks", + base::FEATURE_DISABLED_BY_DEFAULT}; + static jboolean IsInitialized(JNIEnv* env, const JavaParamRef<jclass>& clazz) { return !!base::FeatureList::GetInstance(); }
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index f6e35071..68359b9 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -50,6 +50,7 @@ extern const base::Feature kWebPaymentsModifiers; extern const base::Feature kWebPaymentsSingleAppUiSkip; extern const base::Feature kWebVRCardboardSupport; +extern const base::Feature kXGEOVisibleNetworks; bool RegisterChromeFeatureListJni(JNIEnv* env);
diff --git a/chrome/browser/browsing_data/cache_counter.cc b/chrome/browser/browsing_data/cache_counter.cc index 557be8a3..d131033 100644 --- a/chrome/browser/browsing_data/cache_counter.cc +++ b/chrome/browser/browsing_data/cache_counter.cc
@@ -32,6 +32,8 @@ } void CacheCounter::Count() { + // Cancel existing requests. + weak_ptr_factory_.InvalidateWeakPtrs(); base::WeakPtr<browsing_data::ConditionalCacheCountingHelper> counter = browsing_data::ConditionalCacheCountingHelper::CreateForRange( content::BrowserContext::GetDefaultStoragePartition(profile_),
diff --git a/chrome/browser/browsing_data/media_licenses_counter.cc b/chrome/browser/browsing_data/media_licenses_counter.cc index cd248ad46..9f19f2766 100644 --- a/chrome/browser/browsing_data/media_licenses_counter.cc +++ b/chrome/browser/browsing_data/media_licenses_counter.cc
@@ -63,6 +63,8 @@ void MediaLicensesCounter::Count() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // Cancel existing requests. + weak_ptr_factory_.InvalidateWeakPtrs(); scoped_refptr<storage::FileSystemContext> filesystem_context = make_scoped_refptr( content::BrowserContext::GetDefaultStoragePartition(profile_)
diff --git a/chrome/browser/browsing_data/site_data_counter.cc b/chrome/browser/browsing_data/site_data_counter.cc index 36397fd..8106d3ea 100644 --- a/chrome/browser/browsing_data/site_data_counter.cc +++ b/chrome/browser/browsing_data/site_data_counter.cc
@@ -20,6 +20,8 @@ } void SiteDataCounter::Count() { + // Cancel existing requests. + weak_ptr_factory_.InvalidateWeakPtrs(); base::Time begin = GetPeriodStart(); auto done_callback = base::Bind(&SiteDataCounter::Done, weak_ptr_factory_.GetWeakPtr());
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 71c0017a..3004ed2 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -3127,4 +3127,11 @@ "Enable an experimental change for Location.reload() to trigger a " "hard-reload."; +const char kCaptureThumbnailOnLoadFinishedName[] = + "Capture page thumbnail on load finished"; + +const char kCaptureThumbnailOnLoadFinishedDescription[] = + "Capture a page thumbnail (for use on the New Tab page) when the page load " + "finishes, in addition to other times a thumbnail may be captured."; + } // namespace flag_descriptions
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 437edc3..3074292 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -3398,6 +3398,10 @@ // a hard-reload. extern const char kLocationHardReloadDescription[]; +// Name and description for the capture-thumbnail-on-load-finished flag. +extern const char kCaptureThumbnailOnLoadFinishedName[]; +extern const char kCaptureThumbnailOnLoadFinishedDescription[]; + } // namespace flag_descriptions #endif // CHROME_BROWSER_FLAG_DESCRIPTIONS_H_
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm index 83d4bad..841897d 100644 --- a/chrome/browser/notifications/notification_platform_bridge_mac.mm +++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -128,24 +128,53 @@ return title; } +bool IsPersistentNotification(const Notification& notification) { + return notification.never_timeout() || + notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS; +} + base::string16 CreateNotificationContext(const Notification& notification, bool requires_attribution) { if (!requires_attribution) return notification.context_message(); - base::string16 context = + // Mac OS notifications don't provide a good way to elide the domain (or tell + // you the maximum width of the subtitle field). We have experimentally + // determined the maximum number of characters that fit using the widest + // possible character (m). If the domain fits in those character we show it + // completely. Otherwise we use eTLD + 1. + + // These numbers have been obtained through experimentation on various + // Mac OS platforms. + + // Corresponds to the string "mmmmmmmmmmmmmm" + constexpr size_t kMaxDomainLenghtAlert = 14; + + // Corresponds to the string "mmmmmmmmmmmmmmmmmmmmm" + constexpr size_t kMaxDomainLenghtBanner = 21; + + size_t max_characters = IsPersistentNotification(notification) + ? kMaxDomainLenghtAlert + : kMaxDomainLenghtBanner; + + base::string16 origin = url_formatter::FormatOriginForSecurityDisplay( + url::Origin(notification.origin_url()), + url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); + + if (origin.size() <= max_characters) + return origin; + + // Too long, use etld+1 + base::string16 etldplusone = base::UTF8ToUTF16(net::registry_controlled_domains::GetDomainAndRegistry( notification.origin_url(), net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); // localhost, raw IPs etc. are not handled by GetDomainAndRegistry. - if (context.empty()) { - context = url_formatter::FormatOriginForSecurityDisplay( - url::Origin(notification.origin_url()), - url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); - } + if (etldplusone.empty()) + return origin; - return context; + return etldplusone; } } // namespace @@ -268,9 +297,7 @@ // Send persistent notifications to the XPC service so they // can be displayed as alerts. Chrome itself can only display // banners. - // Progress Notifications are always considered persistent. - if (notification.never_timeout() || - notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) { + if (IsPersistentNotification(notification)) { NSDictionary* dict = [builder buildDictionary]; [alert_dispatcher_ dispatchNotification:dict]; } else {
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm index 81eb7cd..06c4419 100644 --- a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm +++ b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
@@ -434,37 +434,43 @@ notification = CreateBanner("Title", "Context", "https://mail.appspot.com", "Button 1", nullptr); - bridge->Display(NotificationCommon::PERSISTENT, "notification_id2", "profile_id", false, *notification); + notification = CreateBanner("Title", "Context", "https://tests.peter.sh", "Button 1", nullptr); - bridge->Display(NotificationCommon::PERSISTENT, "notification_id3", "profile_id", false, *notification); + notification = CreateBanner( + "Title", "Context", + "https://somereallylongsubdomainthatactuallyisanaliasfortests.peter.sh/", + "Button 1", nullptr); + bridge->Display(NotificationCommon::PERSISTENT, "notification_id4", + "profile_id", false, *notification); + notification = CreateBanner("Title", "Context", "http://localhost:8080", "Button 1", nullptr); - - bridge->Display(NotificationCommon::PERSISTENT, "notification_id4", + bridge->Display(NotificationCommon::PERSISTENT, "notification_id5", "profile_id", false, *notification); notification = CreateBanner("Title", "Context", "https://93.186.186.172", "Button 1", nullptr); - - bridge->Display(NotificationCommon::PERSISTENT, "notification_id5", + bridge->Display(NotificationCommon::PERSISTENT, "notification_id6", "profile_id", false, *notification); NSArray* notifications = [notification_center() deliveredNotifications]; - EXPECT_EQ(5u, [notifications count]); + EXPECT_EQ(6u, [notifications count]); NSUserNotification* delivered_notification = [notifications objectAtIndex:0]; EXPECT_NSEQ(@"test.co.uk", [delivered_notification subtitle]); delivered_notification = [notifications objectAtIndex:1]; EXPECT_NSEQ(@"mail.appspot.com", [delivered_notification subtitle]); delivered_notification = [notifications objectAtIndex:2]; - EXPECT_NSEQ(@"peter.sh", [delivered_notification subtitle]); + EXPECT_NSEQ(@"tests.peter.sh", [delivered_notification subtitle]); delivered_notification = [notifications objectAtIndex:3]; - EXPECT_NSEQ(@"localhost:8080", [delivered_notification subtitle]); + EXPECT_NSEQ(@"peter.sh", [delivered_notification subtitle]); delivered_notification = [notifications objectAtIndex:4]; + EXPECT_NSEQ(@"localhost:8080", [delivered_notification subtitle]); + delivered_notification = [notifications objectAtIndex:5]; EXPECT_NSEQ(@"93.186.186.172", [delivered_notification subtitle]); }
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc index dbe29ea07..cfdd6ff 100644 --- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc +++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -12,6 +12,7 @@ #include "base/test/thread_test_helper.h" #include "base/time/clock.h" #include "base/time/time.h" +#include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h" #include "chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h" @@ -384,9 +385,15 @@ ReportExpectation::Successful({{"report1", RetryStatus::RETRIED}})); } +// Failing on some Win and Mac buildbots. See crbug.com/719138. +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_DontSendOldReports DISABLED_DontSendOldReports +#else +#define MAYBE_DontSendOldReports DontSendOldReports +#endif // CertificateReportingService should ignore reports older than the report TTL. IN_PROC_BROWSER_TEST_F(CertificateReportingServiceBrowserTest, - DontSendOldReports) { + MAYBE_DontSendOldReports) { SetExpectedHistogramCountOnTeardown(5); base::SimpleTestClock* clock = new base::SimpleTestClock();
diff --git a/chrome/browser/thumbnails/thumbnail_tab_helper.cc b/chrome/browser/thumbnails/thumbnail_tab_helper.cc index b710d4cc..a60fd560 100644 --- a/chrome/browser/thumbnails/thumbnail_tab_helper.cc +++ b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
@@ -4,13 +4,13 @@ #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" +#include "base/feature_list.h" #include "build/build_config.h" -#include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/thumbnails/thumbnail_service.h" #include "chrome/browser/thumbnails/thumbnail_service_factory.h" #include "chrome/browser/thumbnails/thumbnailing_algorithm.h" -#include "chrome/browser/thumbnails/thumbnailing_context.h" +#include "chrome/common/chrome_features.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" @@ -18,14 +18,8 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" -#include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/scrollbar_size.h" -#include "ui/gfx/skbitmap_operations.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThumbnailTabHelper); @@ -35,7 +29,7 @@ // -------- // This class provides a service for updating thumbnails to be used in // "Most visited" section of the new tab page. The service can be started -// by StartThumbnailing(). The current algorithm of the service is as +// by UpdateThumbnailIfNecessary(). The current algorithm of the service is as // simple as follows: // // When a renderer is about to be hidden (this usually occurs when the @@ -43,17 +37,21 @@ // thumbnail for the tab rendered by the renderer, if needed. The // heuristics to judge whether or not to update the thumbnail is // implemented in ShouldUpdateThumbnail(). +// If features::kCaptureThumbnailOnLoadFinished is enabled, then a thumbnail +// may also be captured when a page load finishes (subject to the same +// heuristics). using content::RenderViewHost; using content::RenderWidgetHost; using content::WebContents; -using thumbnails::ClipResult; using thumbnails::ThumbnailingContext; using thumbnails::ThumbnailingAlgorithm; ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) : content::WebContentsObserver(contents), + capture_on_load_finished_(base::FeatureList::IsEnabled( + features::kCaptureThumbnailOnLoadFinished)), load_interrupted_(false), weak_factory_(this) { // Even though we deal in RenderWidgetHosts, we only care about its @@ -86,8 +84,7 @@ } } -void ThumbnailTabHelper::RenderViewDeleted( - content::RenderViewHost* render_view_host) { +void ThumbnailTabHelper::RenderViewDeleted(RenderViewHost* render_view_host) { bool registered = registrar_.IsRegistered( this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, content::Source<RenderWidgetHost>(render_view_host->GetWidget())); @@ -102,6 +99,12 @@ load_interrupted_ = false; } +void ThumbnailTabHelper::DidStopLoading() { + if (capture_on_load_finished_) { + UpdateThumbnailIfNecessary(); + } +} + void ThumbnailTabHelper::NavigationStopped() { // This function gets called when the page loading is interrupted by the // stop button. @@ -163,7 +166,7 @@ return; } - scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( + scoped_refptr<ThumbnailingAlgorithm> algorithm( thumbnail_service->GetThumbnailingAlgorithm()); thumbnailing_context_ = new ThumbnailingContext(web_contents(), @@ -185,7 +188,7 @@ } void ThumbnailTabHelper::ProcessCapturedBitmap( - scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm, + scoped_refptr<ThumbnailingAlgorithm> algorithm, const SkBitmap& bitmap, content::ReadbackResponse response) { if (response == content::READBACK_SUCCESS) { @@ -211,9 +214,8 @@ thumbnailing_context_ = nullptr; } -void ThumbnailTabHelper::UpdateThumbnail( - const thumbnails::ThumbnailingContext& context, - const SkBitmap& thumbnail) { +void ThumbnailTabHelper::UpdateThumbnail(const ThumbnailingContext& context, + const SkBitmap& thumbnail) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Feed the constructed thumbnail to the thumbnail service. gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); @@ -224,8 +226,7 @@ CleanUpFromThumbnailGeneration(); } -void ThumbnailTabHelper::RenderViewHostCreated( - content::RenderViewHost* renderer) { +void ThumbnailTabHelper::RenderViewHostCreated(RenderViewHost* renderer) { // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new // RenderView, not RenderViewHost, and there is no good way to get // notifications of RenderViewHosts. So just be tolerant of re-registrations.
diff --git a/chrome/browser/thumbnails/thumbnail_tab_helper.h b/chrome/browser/thumbnails/thumbnail_tab_helper.h index aae839e..b91b05a 100644 --- a/chrome/browser/thumbnails/thumbnail_tab_helper.h +++ b/chrome/browser/thumbnails/thumbnail_tab_helper.h
@@ -38,6 +38,7 @@ // content::WebContentsObserver overrides. void RenderViewDeleted(content::RenderViewHost* render_view_host) override; void DidStartLoading() override; + void DidStopLoading() override; void NavigationStopped() override; // Update the thumbnail of the given tab contents if necessary. @@ -67,6 +68,8 @@ // Indicates that the given widget has changed is visibility. void WidgetHidden(content::RenderWidgetHost* widget); + const bool capture_on_load_finished_; + content::NotificationRegistrar registrar_; scoped_refptr<thumbnails::ThumbnailingContext> thumbnailing_context_;
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 7cd2d31c..521c2036 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -99,6 +99,11 @@ const base::Feature kTabsInCbd{"TabsInCBD", base::FEATURE_DISABLED_BY_DEFAULT}; +// Whether to capture page thumbnails when the page load finishes (in addition +// to any other times this might happen). +const base::Feature kCaptureThumbnailOnLoadFinished{ + "CaptureThumbnailOnLoadFinished", base::FEATURE_DISABLED_BY_DEFAULT}; + // Whether to trigger app banner installability checks on page load. const base::Feature kCheckInstallabilityForBannerOnLoad{ "CheckInstallabilityForBannerOnLoad", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 4aae3c88..306e4f7 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -62,6 +62,8 @@ extern const base::Feature kTabStripKeyboardFocus; #endif // defined(OS_MACOSX) +extern const base::Feature kCaptureThumbnailOnLoadFinished; + extern const base::Feature kCheckInstallabilityForBannerOnLoad; #if defined(OS_WIN)
diff --git a/components/browsing_data/core/counters/browsing_data_counter.h b/components/browsing_data/core/counters/browsing_data_counter.h index d7fbb95..d7d532a 100644 --- a/components/browsing_data/core/counters/browsing_data_counter.h +++ b/components/browsing_data/core/counters/browsing_data_counter.h
@@ -128,7 +128,8 @@ // Called after the class is initialized by calling |Init|. virtual void OnInitialized(); - // Count the data. + // Count the data. Call ReportResult() when finished. Tasks that are still + // running should be cancelled to avoid reporting old results. virtual void Count() = 0; // State transition methods.
diff --git a/components/browsing_data/core/counters/history_counter.cc b/components/browsing_data/core/counters/history_counter.cc index 6fcb1c1..0061bb1 100644 --- a/components/browsing_data/core/counters/history_counter.cc +++ b/components/browsing_data/core/counters/history_counter.cc
@@ -55,6 +55,8 @@ // Reset the state. cancelable_task_tracker_.TryCancelAll(); web_history_request_.reset(); + weak_ptr_factory_.InvalidateWeakPtrs(); + has_synced_visits_ = false; // Count the locally stored items.
diff --git a/components/favicon/core/favicon_handler_unittest.cc b/components/favicon/core/favicon_handler_unittest.cc index 6f3f041e..584484a6 100644 --- a/components/favicon/core/favicon_handler_unittest.cc +++ b/components/favicon/core/favicon_handler_unittest.cc
@@ -17,6 +17,7 @@ #include "base/strings/stringprintf.h" #include "base/test/histogram_tester.h" #include "base/test/scoped_task_environment.h" +#include "base/test/test_simple_task_runner.h" #include "components/favicon/core/favicon_driver.h" #include "components/favicon/core/test/mock_favicon_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -229,7 +230,8 @@ // particular URL, the callback is called with empty database results. class FakeFaviconService { public: - FakeFaviconService() = default; + FakeFaviconService() + : manual_callback_task_runner_(new base::TestSimpleTaskRunner()) {} // Stores favicon with bitmap data in |results| at |page_url| and |icon_url|. void Store(const GURL& page_url, @@ -272,6 +274,28 @@ return GetFaviconForPageOrIconURL(icon_url, callback, tracker); } + // Disables automatic callback for |url|. This is useful for emulating a + // DB lookup taking a long time. The callback for + // GetFaviconForPageOrIconURL() will be stored in |manual_callback_|. + void SetRunCallbackManuallyForUrl(const GURL& url) { + manual_callback_url_ = url; + } + + // Returns whether an ongoing lookup exists for a url previously selected + // via SetRunCallbackManuallyForUrl(). + bool HasPendingManualCallback() { + return manual_callback_task_runner_->HasPendingTask(); + } + + // Triggers the response for a lookup previously selected for manual + // triggering via SetRunCallbackManuallyForUrl(). + bool RunCallbackManually() { + if (!HasPendingManualCallback()) + return false; + manual_callback_task_runner_->RunPendingTasks(); + return true; + } + private: base::CancelableTaskTracker::TaskId GetFaviconForPageOrIconURL( const GURL& page_or_icon_url, @@ -279,14 +303,31 @@ base::CancelableTaskTracker* tracker) { db_requests_.push_back(page_or_icon_url); - return tracker->PostTask(base::ThreadTaskRunnerHandle::Get().get(), - FROM_HERE, - base::Bind(callback, results_[page_or_icon_url])); + base::Closure bound_callback = + base::Bind(callback, results_[page_or_icon_url]); + + if (page_or_icon_url != manual_callback_url_) { + return tracker->PostTask(base::ThreadTaskRunnerHandle::Get().get(), + FROM_HERE, bound_callback); + } + + // We use PostTaskAndReply() to cause |callback| being run in the current + // TaskRunner. + return tracker->PostTaskAndReply(manual_callback_task_runner_.get(), + FROM_HERE, base::Bind(&base::DoNothing), + bound_callback); } std::map<GURL, std::vector<favicon_base::FaviconRawBitmapResult>> results_; URLVector db_requests_; + // URL to disable automatic callbacks for. + GURL manual_callback_url_; + + // Callback for GetFaviconForPageOrIconURL() request for + // |manual_callback_url_|. + scoped_refptr<base::TestSimpleTaskRunner> manual_callback_task_runner_; + DISALLOW_COPY_AND_ASSIGN(FakeFaviconService); }; @@ -422,24 +463,35 @@ // - FaviconService::GetFaviconForPageURL() callback returns before // FaviconHandler::OnUpdateFaviconURL() is called. TEST_F(FaviconHandlerTest, DownloadUnknownFaviconIfCandidatesSlower) { + // Defer the database lookup completion to control the exact timing. + favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL); + + EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0); + EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0); + + FaviconHandler handler(&favicon_service_, &delegate_, + FaviconDriverObserver::NON_TOUCH_16_DIP); + handler.FetchFavicon(kPageURL); + base::RunLoop().RunUntilIdle(); + // Database lookup for |kPageURL| is ongoing. + ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback()); + // Causes FaviconService lookups be faster than OnUpdateFaviconURL(). + ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually()); + ASSERT_TRUE(VerifyAndClearExpectations()); + EXPECT_CALL(favicon_service_, SetFavicons(kPageURL, kIconURL16x16, FAVICON, ImageSizeIs(16, 16))); EXPECT_CALL(delegate_, OnFaviconUpdated( kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP, kIconURL16x16, /*icon_url_changed=*/true, _)); - - FaviconHandler handler(&favicon_service_, &delegate_, - FaviconDriverObserver::NON_TOUCH_16_DIP); - handler.FetchFavicon(kPageURL); - // Causes FaviconService lookups be faster than OnUpdateFaviconURL(). - base::RunLoop().RunUntilIdle(); + // Feed in favicons now that the database lookup is completed. handler.OnUpdateFaviconURL(kPageURL, {FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)}); base::RunLoop().RunUntilIdle(); - EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16)); EXPECT_THAT(favicon_service_.fake()->db_requests(), - ElementsAre(kPageURL, kIconURL16x16)); + ElementsAre(kIconURL16x16)); + EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16)); } // Test that the FaviconHandler process finishes when: @@ -448,20 +500,34 @@ // - FaviconService::GetFaviconForPageURL() callback returns after // FaviconHandler::OnUpdateFaviconURL() is called. TEST_F(FaviconHandlerTest, DownloadUnknownFaviconIfCandidatesFaster) { - EXPECT_CALL(favicon_service_, SetFavicons(kPageURL, kIconURL16x16, FAVICON, - ImageSizeIs(16, 16))); - EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _)); + // Defer the database lookup completion to control the exact timing. + favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL); + + EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0); + EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0); FaviconHandler handler(&favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP); handler.FetchFavicon(kPageURL); - ASSERT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kPageURL)); - - // Feed in favicons without processing posted tasks (RunUntilIdle()). + base::RunLoop().RunUntilIdle(); + // Feed in favicons before completing the database lookup. handler.OnUpdateFaviconURL(kPageURL, {FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)}); + + ASSERT_TRUE(VerifyAndClearExpectations()); + // Database lookup for |kPageURL| is ongoing. + ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback()); + + EXPECT_CALL(favicon_service_, SetFavicons(kPageURL, kIconURL16x16, FAVICON, + ImageSizeIs(16, 16))); + EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _)); + + // Complete the lookup for |kPageURL|. + ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually()); base::RunLoop().RunUntilIdle(); + EXPECT_THAT(favicon_service_.fake()->db_requests(), + ElementsAre(kIconURL16x16)); EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16)); } @@ -656,8 +722,8 @@ } // Test that sending an icon URL update identical to the previous icon URL -// update is a no-op. -TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileProcessingShouldBeNoop) { +// update during image download is a no-op. +TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDownloadingShouldBeNoop) { const GURL kSlowLoadingIconURL("http://www.google.com/slow_favicon"); const std::vector<FaviconURL> favicon_urls = { @@ -682,6 +748,8 @@ // despite the ongoing download. handler->OnUpdateFaviconURL(kPageURL, favicon_urls); base::RunLoop().RunUntilIdle(); + EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty()); + EXPECT_THAT(delegate_.downloads(), IsEmpty()); // Complete the download. EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)); @@ -691,6 +759,40 @@ EXPECT_THAT(delegate_.downloads(), IsEmpty()); } +// Test that sending an icon URL update identical to the previous icon URL +// update during a database lookup is a no-op. +TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDatabaseLookupShouldBeNoop) { + const std::vector<FaviconURL> favicon_urls = { + FaviconURL(kIconURL64x64, FAVICON, kEmptySizes), + }; + + favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL64x64); + + std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates( + FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls); + + // Ongoing database lookup. + ASSERT_THAT(favicon_service_.fake()->db_requests(), + ElementsAre(kPageURL, kIconURL64x64)); + ASSERT_THAT(delegate_.downloads(), IsEmpty()); + ASSERT_TRUE(VerifyAndClearExpectations()); + ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback()); + + // Calling OnUpdateFaviconURL() with the same icon URLs should have no effect, + // despite the ongoing DB lookup. + handler->OnUpdateFaviconURL(kPageURL, favicon_urls); + base::RunLoop().RunUntilIdle(); + EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty()); + EXPECT_THAT(delegate_.downloads(), IsEmpty()); + + // Complete the lookup. + EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)); + EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)); + EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually()); + base::RunLoop().RunUntilIdle(); + EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64)); +} + // Test that calling OnUpdateFaviconUrl() with the same icon URLs as before is a // no-op. This is important because OnUpdateFaviconUrl() is called when the page // finishes loading. This can occur several times for pages with iframes.
diff --git a/components/metrics/execution_phase.cc b/components/metrics/execution_phase.cc index 98f9aee..992ef82 100644 --- a/components/metrics/execution_phase.cc +++ b/components/metrics/execution_phase.cc
@@ -5,12 +5,15 @@ #include "components/metrics/execution_phase.h" #include "build/build_config.h" -#include "components/browser_watcher/stability_data_names.h" -#include "components/browser_watcher/stability_debugging.h" #include "components/metrics/metrics_pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" +#if defined(OS_WIN) +#include "components/browser_watcher/stability_data_names.h" +#include "components/browser_watcher/stability_debugging.h" +#endif // defined(OS_WIN) + namespace metrics { ExecutionPhaseManager::ExecutionPhaseManager(PrefService* local_state)
diff --git a/components/suggestions/suggestions_service_impl.h b/components/suggestions/suggestions_service_impl.h index ee0b2fd..b2ce162 100644 --- a/components/suggestions/suggestions_service_impl.h +++ b/components/suggestions/suggestions_service_impl.h
@@ -105,6 +105,10 @@ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, ClearBlacklist); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UndoBlacklistURL); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, + UndoBlacklistURLFailsIfNotInBlacklist); + FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, + UndoBlacklistURLFailsIfAlreadyCandidate); + FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, GetBlacklistedUrlBlacklistRequest); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, CheckDefaultTimeStamps);
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc index 2a9ab77..ef75e2e 100644 --- a/components/suggestions/suggestions_service_impl_unittest.cc +++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -186,7 +186,6 @@ signin_client_(&pref_service_), signin_manager_(&signin_client_, &account_tracker_), factory_(nullptr, base::Bind(&CreateURLFetcher)), - mock_sync_service_(nullptr), mock_thumbnail_manager_(nullptr), mock_blacklist_store_(nullptr), test_suggestions_store_(nullptr) { @@ -203,86 +202,35 @@ void SetUp() override { request_context_ = new net::TestURLRequestContextGetter(io_message_loop_.task_runner()); - } - std::unique_ptr<SuggestionsServiceImpl> CreateSuggestionsServiceWithMocks() { - mock_sync_service_ = base::MakeUnique<MockSyncService>(); - EXPECT_CALL(*mock_sync_service_, CanSyncStart()) + EXPECT_CALL(mock_sync_service_, CanSyncStart()) .Times(AnyNumber()) .WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_sync_service_, IsSyncActive()) + EXPECT_CALL(mock_sync_service_, IsSyncActive()) .Times(AnyNumber()) .WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_sync_service_, ConfigurationDone()) + EXPECT_CALL(mock_sync_service_, ConfigurationDone()) .Times(AnyNumber()) .WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_sync_service_, GetActiveDataTypes()) + EXPECT_CALL(mock_sync_service_, GetActiveDataTypes()) .Times(AnyNumber()) .WillRepeatedly( Return(syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES))); - // These objects are owned by the returned SuggestionsService, but we keep - // the pointer around for testing. - // TODO(treib): This is broken - if the test destroys the SuggestionsService - // (which it can easily do, since it has an owning pointer), we'll be left - // with dangling pointers here. + // These objects are owned by the SuggestionsService, but we keep the + // pointers around for testing. test_suggestions_store_ = new TestSuggestionsStore(); mock_thumbnail_manager_ = new StrictMock<MockImageManager>(); mock_blacklist_store_ = new StrictMock<MockBlacklistStore>(); - return base::MakeUnique<SuggestionsServiceImpl>( - &signin_manager_, &token_service_, mock_sync_service_.get(), + suggestions_service_ = base::MakeUnique<SuggestionsServiceImpl>( + &signin_manager_, &token_service_, &mock_sync_service_, request_context_.get(), base::WrapUnique(test_suggestions_store_), base::WrapUnique(mock_thumbnail_manager_), base::WrapUnique(mock_blacklist_store_)); } - // Helper for Undo failure tests. Depending on |is_uploaded|, tests either - // the case where the URL is no longer in the local blacklist or the case - // in which it's not yet candidate for upload. - void UndoBlacklistURLFailsHelper(bool is_uploaded) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - // Ensure scheduling the request doesn't happen before undo. - base::TimeDelta delay = base::TimeDelta::FromHours(1); - suggestions_service->set_blacklist_delay(delay); - - auto subscription = suggestions_service->AddCallback(base::Bind( - &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); - - SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); - GURL blacklisted_url(kBlacklistedUrl); - - // Blacklist expectations. - EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_thumbnail_manager_, - Initialize(EqualsProto(suggestions_profile))); - EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); - EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) - .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); - // Undo expectations. - if (is_uploaded) { - // URL is not in local blacklist. - EXPECT_CALL(*mock_blacklist_store_, - GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) - .WillOnce(Return(false)); - } else { - // URL is not yet candidate for upload. - base::TimeDelta negative_delay = base::TimeDelta::FromHours(-1); - EXPECT_CALL(*mock_blacklist_store_, - GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) - .WillOnce(DoAll(SetArgPointee<1>(negative_delay), Return(true))); - } - - EXPECT_TRUE(suggestions_service->BlacklistURL(blacklisted_url)); - EXPECT_FALSE(suggestions_service->UndoBlacklistURL(blacklisted_url)); - - EXPECT_EQ(1, suggestions_data_callback_count_); - } - - bool HasPendingSuggestionsRequest( - SuggestionsServiceImpl* suggestions_service) { - return !!suggestions_service->pending_request_.get(); + bool HasPendingSuggestionsRequest() const { + return !!suggestions_service_->pending_request_.get(); } protected: @@ -293,21 +241,21 @@ FakeSigninManagerBase signin_manager_; net::FakeURLFetcherFactory factory_; FakeProfileOAuth2TokenService token_service_; - std::unique_ptr<MockSyncService> mock_sync_service_; - // Only used if the SuggestionsService is built with mocks. Not owned. + MockSyncService mock_sync_service_; + scoped_refptr<net::TestURLRequestContextGetter> request_context_; + // Owned by the SuggestionsService. MockImageManager* mock_thumbnail_manager_; MockBlacklistStore* mock_blacklist_store_; TestSuggestionsStore* test_suggestions_store_; - scoped_refptr<net::TestURLRequestContextGetter> request_context_; + + std::unique_ptr<SuggestionsServiceImpl> suggestions_service_; private: DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceTest); }; TEST_F(SuggestionsServiceTest, FetchSuggestionsData) { - std::unique_ptr<SuggestionsService> suggestions_service( - CreateSuggestionsServiceWithMocks()); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); @@ -324,7 +272,7 @@ .WillOnce(Return(false)); // Send the request. The data should be returned to the callback. - suggestions_service->FetchSuggestionsData(); + suggestions_service_->FetchSuggestionsData(); // Let the network request run. base::RunLoop().RunUntilIdle(); @@ -340,9 +288,7 @@ } TEST_F(SuggestionsServiceTest, IgnoresNoopSyncChange) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); @@ -351,7 +297,7 @@ net::HTTP_OK, net::URLRequestStatus::SUCCESS); // An no-op change should not result in a suggestions refresh. - suggestions_service->OnStateChanged(mock_sync_service_.get()); + suggestions_service_->OnStateChanged(&mock_sync_service_); // Let any network request run (there shouldn't be one). base::RunLoop().RunUntilIdle(); @@ -361,9 +307,7 @@ } TEST_F(SuggestionsServiceTest, IgnoresUninterestingSyncChange) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); @@ -373,11 +317,11 @@ // An uninteresting change should not result in a network request (the // SyncState is INITIALIZED_ENABLED_HISTORY before and after). - EXPECT_CALL(*mock_sync_service_, GetActiveDataTypes()) + EXPECT_CALL(mock_sync_service_, GetActiveDataTypes()) .Times(AnyNumber()) .WillRepeatedly(Return(syncer::ModelTypeSet( syncer::HISTORY_DELETE_DIRECTIVES, syncer::BOOKMARKS))); - suggestions_service->OnStateChanged(mock_sync_service_.get()); + suggestions_service_->OnStateChanged(&mock_sync_service_); // Let any network request run (there shouldn't be one). base::RunLoop().RunUntilIdle(); @@ -387,18 +331,15 @@ } TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - EXPECT_CALL(*mock_sync_service_, IsSyncActive()) - .WillRepeatedly(Return(false)); - suggestions_service->OnStateChanged(mock_sync_service_.get()); + EXPECT_CALL(mock_sync_service_, IsSyncActive()).WillRepeatedly(Return(false)); + suggestions_service_->OnStateChanged(&mock_sync_service_); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); // Try to fetch suggestions. Since sync is not active, no network request // should be sent. - suggestions_service->FetchSuggestionsData(); + suggestions_service_->FetchSuggestionsData(); // Let any network request run. base::RunLoop().RunUntilIdle(); @@ -414,17 +355,14 @@ } TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - EXPECT_CALL(*mock_sync_service_, CanSyncStart()) - .WillRepeatedly(Return(false)); + EXPECT_CALL(mock_sync_service_, CanSyncStart()).WillRepeatedly(Return(false)); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); // Tell SuggestionsService that the sync state changed. The cache should be // cleared and empty data returned to the callback. - suggestions_service->OnStateChanged(mock_sync_service_.get()); + suggestions_service_->OnStateChanged(&mock_sync_service_); // Ensure that CheckCallback ran once with empty data. EXPECT_EQ(1, suggestions_data_callback_count_); @@ -432,7 +370,7 @@ // Try to fetch suggestions. Since sync is not active, no network request // should be sent. - suggestions_service->FetchSuggestionsData(); + suggestions_service_->FetchSuggestionsData(); // Let any network request run. base::RunLoop().RunUntilIdle(); @@ -444,30 +382,24 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoAccessToken) { token_service_.set_auto_post_fetch_response_on_message_loop(false); - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) .WillOnce(Return(false)); - suggestions_service->FetchSuggestionsData(); + suggestions_service_->FetchSuggestionsData(); token_service_.IssueErrorForAllPendingRequests(GoogleServiceAuthError( GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS)); // No network request should be sent. base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(HasPendingSuggestionsRequest(suggestions_service.get())); + EXPECT_FALSE(HasPendingSuggestionsRequest()); EXPECT_EQ(0, suggestions_data_callback_count_); } TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingError) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - // Fake a request error. factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(), "irrelevant", net::HTTP_OK, @@ -477,7 +409,7 @@ .WillOnce(Return(false)); // Send the request. Empty data will be returned to the callback. - suggestions_service->IssueRequestIfNoneOngoing( + suggestions_service_->IssueRequestIfNoneOngoing( SuggestionsServiceImpl::BuildSuggestionsURL()); // (Testing only) wait until suggestion fetch is complete. @@ -485,9 +417,6 @@ } TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - // Fake a non-200 response code. factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(), "irrelevant", net::HTTP_BAD_REQUEST, @@ -498,7 +427,7 @@ .WillOnce(Return(false)); // Send the request. Empty data will be returned to the callback. - suggestions_service->IssueRequestIfNoneOngoing( + suggestions_service_->IssueRequestIfNoneOngoing( SuggestionsServiceImpl::BuildSuggestionsURL()); // (Testing only) wait until suggestion fetch is complete. @@ -510,12 +439,10 @@ } TEST_F(SuggestionsServiceTest, BlacklistURL) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0); - suggestions_service->set_blacklist_delay(no_delay); + suggestions_service_->set_blacklist_delay(no_delay); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); GURL blacklisted_url(kBlacklistedUrl); @@ -538,7 +465,7 @@ EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url))) .WillOnce(Return(true)); - EXPECT_TRUE(suggestions_service->BlacklistURL(blacklisted_url)); + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); EXPECT_EQ(1, suggestions_data_callback_count_); // Wait on the upload task, the blacklist request and the next blacklist @@ -557,29 +484,24 @@ } TEST_F(SuggestionsServiceTest, BlacklistURLFails) { - std::unique_ptr<SuggestionsService> suggestions_service( - CreateSuggestionsServiceWithMocks()); - - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); GURL blacklisted_url(kBlacklistedUrl); EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) .WillOnce(Return(false)); - EXPECT_FALSE(suggestions_service->BlacklistURL(blacklisted_url)); + EXPECT_FALSE(suggestions_service_->BlacklistURL(blacklisted_url)); EXPECT_EQ(0, suggestions_data_callback_count_); } // Initial blacklist request fails, triggering a second which succeeds. TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0); - suggestions_service->set_blacklist_delay(no_delay); + suggestions_service_->set_blacklist_delay(no_delay); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); GURL blacklisted_url(kBlacklistedUrl); @@ -616,7 +538,7 @@ .WillOnce(Return(true)); // Blacklist call, first request attempt. - EXPECT_TRUE(suggestions_service->BlacklistURL(blacklisted_url)); + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); EXPECT_EQ(1, suggestions_data_callback_count_); // Wait for the first scheduling, the first request, the second scheduling, @@ -633,13 +555,11 @@ } TEST_F(SuggestionsServiceTest, UndoBlacklistURL) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); // Ensure scheduling the request doesn't happen before undo. base::TimeDelta delay = base::TimeDelta::FromHours(1); - suggestions_service->set_blacklist_delay(delay); + suggestions_service_->set_blacklist_delay(delay); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); @@ -661,20 +581,18 @@ EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url))) .WillOnce(Return(true)); - EXPECT_TRUE(suggestions_service->BlacklistURL(blacklisted_url)); - EXPECT_TRUE(suggestions_service->UndoBlacklistURL(blacklisted_url)); + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); + EXPECT_TRUE(suggestions_service_->UndoBlacklistURL(blacklisted_url)); EXPECT_EQ(2, suggestions_data_callback_count_); } TEST_F(SuggestionsServiceTest, ClearBlacklist) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); // Ensure scheduling the request doesn't happen before undo. base::TimeDelta delay = base::TimeDelta::FromHours(1); - suggestions_service->set_blacklist_delay(delay); + suggestions_service_->set_blacklist_delay(delay); - auto subscription = suggestions_service->AddCallback(base::Bind( + auto subscription = suggestions_service_->AddCallback(base::Bind( &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); @@ -696,18 +614,73 @@ .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); EXPECT_CALL(*mock_blacklist_store_, ClearBlacklist()); - EXPECT_TRUE(suggestions_service->BlacklistURL(blacklisted_url)); - suggestions_service->ClearBlacklist(); + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); + suggestions_service_->ClearBlacklist(); EXPECT_EQ(2, suggestions_data_callback_count_); } TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfNotInBlacklist) { - UndoBlacklistURLFailsHelper(true); + // Ensure scheduling the request doesn't happen before undo. + base::TimeDelta delay = base::TimeDelta::FromHours(1); + suggestions_service_->set_blacklist_delay(delay); + + auto subscription = suggestions_service_->AddCallback(base::Bind( + &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); + + SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); + GURL blacklisted_url(kBlacklistedUrl); + + // Blacklist expectations. + EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(suggestions_profile))); + EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); + EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) + .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); + + // URL is not in local blacklist. + EXPECT_CALL(*mock_blacklist_store_, + GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) + .WillOnce(Return(false)); + + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); + EXPECT_FALSE(suggestions_service_->UndoBlacklistURL(blacklisted_url)); + + EXPECT_EQ(1, suggestions_data_callback_count_); } TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfAlreadyCandidate) { - UndoBlacklistURLFailsHelper(false); + // Ensure scheduling the request doesn't happen before undo. + base::TimeDelta delay = base::TimeDelta::FromHours(1); + suggestions_service_->set_blacklist_delay(delay); + + auto subscription = suggestions_service_->AddCallback(base::Bind( + &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); + + SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); + GURL blacklisted_url(kBlacklistedUrl); + + // Blacklist expectations. + EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(suggestions_profile))); + EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); + EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) + .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); + + // URL is not yet candidate for upload. + base::TimeDelta negative_delay = base::TimeDelta::FromHours(-1); + EXPECT_CALL(*mock_blacklist_store_, + GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) + .WillOnce(DoAll(SetArgPointee<1>(negative_delay), Return(true))); + + EXPECT_TRUE(suggestions_service_->BlacklistURL(blacklisted_url)); + EXPECT_FALSE(suggestions_service_->UndoBlacklistURL(blacklisted_url)); + + EXPECT_EQ(1, suggestions_data_callback_count_); } TEST_F(SuggestionsServiceTest, GetBlacklistedUrlNotBlacklistRequest) { @@ -737,49 +710,42 @@ } TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); - base::TimeDelta initial_delay = suggestions_service->blacklist_delay(); + base::TimeDelta initial_delay = suggestions_service_->blacklist_delay(); // Delay unchanged on success. - suggestions_service->UpdateBlacklistDelay(true); - EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay()); + suggestions_service_->UpdateBlacklistDelay(true); + EXPECT_EQ(initial_delay, suggestions_service_->blacklist_delay()); // Delay increases on failure. - suggestions_service->UpdateBlacklistDelay(false); - EXPECT_GT(suggestions_service->blacklist_delay(), initial_delay); + suggestions_service_->UpdateBlacklistDelay(false); + EXPECT_GT(suggestions_service_->blacklist_delay(), initial_delay); // Delay resets on success. - suggestions_service->UpdateBlacklistDelay(true); - EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay()); + suggestions_service_->UpdateBlacklistDelay(true); + EXPECT_EQ(initial_delay, suggestions_service_->blacklist_delay()); } TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) { - std::unique_ptr<SuggestionsServiceImpl> suggestions_service( - CreateSuggestionsServiceWithMocks()); SuggestionsProfile suggestions = CreateSuggestionsProfileWithExpiryTimestamps(); - suggestions_service->SetDefaultExpiryTimestamp(&suggestions, - kTestDefaultExpiry); + suggestions_service_->SetDefaultExpiryTimestamp(&suggestions, + kTestDefaultExpiry); EXPECT_EQ(kTestSetExpiry, suggestions.suggestions(0).expiry_ts()); EXPECT_EQ(kTestDefaultExpiry, suggestions.suggestions(1).expiry_ts()); } TEST_F(SuggestionsServiceTest, GetPageThumbnail) { - std::unique_ptr<SuggestionsService> suggestions_service( - CreateSuggestionsServiceWithMocks()); - GURL test_url(kTestUrl); GURL thumbnail_url("https://www.thumbnails.com/thumb.jpg"); base::Callback<void(const GURL&, const gfx::Image&)> dummy_callback; EXPECT_CALL(*mock_thumbnail_manager_, GetImageForURL(test_url, _)); - suggestions_service->GetPageThumbnail(test_url, dummy_callback); + suggestions_service_->GetPageThumbnail(test_url, dummy_callback); EXPECT_CALL(*mock_thumbnail_manager_, AddImageURL(test_url, thumbnail_url)); EXPECT_CALL(*mock_thumbnail_manager_, GetImageForURL(test_url, _)); - suggestions_service->GetPageThumbnailWithURL(test_url, thumbnail_url, - dummy_callback); + suggestions_service_->GetPageThumbnailWithURL(test_url, thumbnail_url, + dummy_callback); } } // namespace suggestions
diff --git a/components/tracing/common/process_metrics_memory_dump_provider.cc b/components/tracing/common/process_metrics_memory_dump_provider.cc index 3f2abde..7db23920 100644 --- a/components/tracing/common/process_metrics_memory_dump_provider.cc +++ b/components/tracing/common/process_metrics_memory_dump_provider.cc
@@ -180,15 +180,18 @@ return num_valid_regions; } -bool GetResidentSizeFromStatmFile(int fd, uint64_t* resident_pages) { +bool GetResidentAndSharedPagesFromStatmFile(int fd, + uint64_t* resident_pages, + uint64_t* shared_pages) { lseek(fd, 0, SEEK_SET); char line[kMaxLineSize]; int res = read(fd, line, kMaxLineSize - 1); if (res <= 0) return false; line[res] = '\0'; - int num_scanned = sscanf(line, "%*s %" SCNu64, resident_pages); - return num_scanned == 1; + int num_scanned = + sscanf(line, "%*s %" SCNu64 " %" SCNu64, resident_pages, shared_pages); + return num_scanned == 2; } #endif // defined(OS_LINUX) || defined(OS_ANDROID) @@ -633,6 +636,29 @@ uint64_t peak_rss_bytes = 0; +#if defined(OS_LINUX) || defined(OS_ANDROID) + auto& footprint = pmd->process_totals()->GetPlatformPrivateFootprint(); + + base::ScopedFD autoclose; + int statm_fd = fast_polling_statm_fd_.get(); + if (statm_fd == -1) { + autoclose = OpenStatm(); + statm_fd = autoclose.get(); + } + if (statm_fd == -1) + return false; + const static size_t page_size = base::GetPageSize(); + uint64_t resident_pages; + uint64_t shared_pages; + bool success = GetResidentAndSharedPagesFromStatmFile( + statm_fd, &resident_pages, &shared_pages); + if (!success) + return false; + + // TODO(hjd): Implement swap in the next CL. + footprint.rss_anon_bytes = (resident_pages - shared_pages) * page_size; +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + #if !defined(OS_IOS) peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize(); #if defined(OS_LINUX) || defined(OS_ANDROID) @@ -676,31 +702,41 @@ return true; } +#if defined(OS_LINUX) || defined(OS_ANDROID) +base::ScopedFD ProcessMetricsMemoryDumpProvider::OpenStatm() { + std::string name = + "/proc/" + + (process_ == base::kNullProcessId ? "self" + : base::IntToString(process_)) + + "/statm"; + base::ScopedFD fd = base::ScopedFD(open(name.c_str(), O_RDONLY)); + DCHECK(fd.is_valid()); + return fd; +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + void ProcessMetricsMemoryDumpProvider::PollFastMemoryTotal( uint64_t* memory_total) { *memory_total = 0; #if defined(OS_LINUX) || defined(OS_ANDROID) + int statm_fd = fast_polling_statm_fd_for_testing; if (statm_fd == -1) { - if (!fast_polling_statm_fd_.is_valid()) { - std::string name = "/proc/" + (process_ == base::kNullProcessId - ? "self" - : base::IntToString(process_)) + - "/statm"; - fast_polling_statm_fd_.reset(open(name.c_str(), O_RDONLY)); - DCHECK(fast_polling_statm_fd_.is_valid()); - } + if (!fast_polling_statm_fd_.is_valid()) + fast_polling_statm_fd_ = OpenStatm(); statm_fd = fast_polling_statm_fd_.get(); + if (statm_fd == -1) + return; } - if (statm_fd == -1) - return; - uint64_t rss_pages = 0; - if (!GetResidentSizeFromStatmFile(statm_fd, &rss_pages)) + uint64_t resident_pages = 0; + uint64_t ignored_shared_pages = 0; + if (!GetResidentAndSharedPagesFromStatmFile(statm_fd, &resident_pages, + &ignored_shared_pages)) return; static size_t page_size = base::GetPageSize(); - *memory_total = rss_pages * page_size; + *memory_total = resident_pages * page_size; #else *memory_total = process_metrics_->GetWorkingSetSize(); #endif
diff --git a/components/tracing/common/process_metrics_memory_dump_provider.h b/components/tracing/common/process_metrics_memory_dump_provider.h index 730a9be..d3c5334 100644 --- a/components/tracing/common/process_metrics_memory_dump_provider.h +++ b/components/tracing/common/process_metrics_memory_dump_provider.h
@@ -75,6 +75,8 @@ static int fast_polling_statm_fd_for_testing; base::ScopedFD fast_polling_statm_fd_; + + base::ScopedFD OpenStatm(); #endif base::ProcessId process_;
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc index fc720a7..ed78781 100644 --- a/content/browser/frame_host/render_widget_host_view_guest.cc +++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -13,7 +13,6 @@ #include "base/message_loop/message_loop.h" #include "build/build_config.h" #include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_manager.h" #include "cc/surfaces/surface_sequence.h" #include "content/browser/browser_plugin/browser_plugin_guest.h"
diff --git a/content/public/renderer/resource_fetcher.h b/content/public/renderer/resource_fetcher.h index ba75a4e..6f6c21e 100644 --- a/content/public/renderer/resource_fetcher.h +++ b/content/public/renderer/resource_fetcher.h
@@ -18,7 +18,7 @@ } namespace blink { -class WebFrame; +class WebLocalFrame; class WebURLResponse; } @@ -51,7 +51,7 @@ // Starts the request using the specified frame. Calls |callback| when // done. - virtual void Start(blink::WebFrame* frame, + virtual void Start(blink::WebLocalFrame* frame, blink::WebURLRequest::RequestContext request_context, const Callback& callback) = 0;
diff --git a/content/renderer/fetchers/resource_fetcher_impl.cc b/content/renderer/fetchers/resource_fetcher_impl.cc index 037e0ec..7b6a0e8 100644 --- a/content/renderer/fetchers/resource_fetcher_impl.cc +++ b/content/renderer/fetchers/resource_fetcher_impl.cc
@@ -20,8 +20,8 @@ #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" #include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" namespace content { @@ -171,12 +171,14 @@ } void ResourceFetcherImpl::Start( - blink::WebFrame* frame, + blink::WebLocalFrame* frame, blink::WebURLRequest::RequestContext request_context, const Callback& callback) { DCHECK(!loader_); DCHECK(!client_); DCHECK(!request_.IsNull()); + DCHECK(frame); + DCHECK(!frame->GetDocument().IsNull()); if (!request_.HttpBody().IsNull()) DCHECK_NE("GET", request_.HttpMethod().Utf8()) << "GETs can't have bodies.";
diff --git a/content/renderer/fetchers/resource_fetcher_impl.h b/content/renderer/fetchers/resource_fetcher_impl.h index fb13047..af0be03e 100644 --- a/content/renderer/fetchers/resource_fetcher_impl.h +++ b/content/renderer/fetchers/resource_fetcher_impl.h
@@ -18,7 +18,7 @@ class GURL; namespace blink { -class WebFrame; +class WebLocalFrame; class WebURLLoader; } @@ -30,7 +30,7 @@ void SetMethod(const std::string& method) override; void SetBody(const std::string& body) override; void SetHeader(const std::string& header, const std::string& value) override; - void Start(blink::WebFrame* frame, + void Start(blink::WebLocalFrame* frame, blink::WebURLRequest::RequestContext request_context, const Callback& callback) override; void SetTimeout(const base::TimeDelta& timeout) override;
diff --git a/content/renderer/mojo_context_state.cc b/content/renderer/mojo_context_state.cc index 8296cd4..1263c49 100644 --- a/content/renderer/mojo_context_state.cc +++ b/content/renderer/mojo_context_state.cc
@@ -29,7 +29,7 @@ #include "mojo/public/js/constants.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" -#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptSource.h" using v8::Context; @@ -109,7 +109,7 @@ } std::string GetModulePrefixForBindingsType(MojoBindingsType bindings_type, - blink::WebFrame* frame) { + blink::WebLocalFrame* frame) { switch (bindings_type) { case MojoBindingsType::FOR_WEB_UI: return frame->GetSecurityOrigin().ToString().Utf8() + "/"; @@ -124,7 +124,7 @@ } // namespace -MojoContextState::MojoContextState(blink::WebFrame* frame, +MojoContextState::MojoContextState(blink::WebLocalFrame* frame, v8::Local<v8::Context> context, MojoBindingsType bindings_type) : frame_(frame),
diff --git a/content/renderer/mojo_context_state.h b/content/renderer/mojo_context_state.h index 1af69b7..7b67ee3f 100644 --- a/content/renderer/mojo_context_state.h +++ b/content/renderer/mojo_context_state.h
@@ -15,7 +15,7 @@ #include "v8/include/v8.h" namespace blink { -class WebFrame; +class WebLocalFrame; class WebURLResponse; } @@ -29,7 +29,7 @@ // by way of gin. Non-builtin modules are downloaded by way of ResourceFetchers. class MojoContextState : public gin::ModuleRegistryObserver { public: - MojoContextState(blink::WebFrame* frame, + MojoContextState(blink::WebLocalFrame* frame, v8::Local<v8::Context> context, MojoBindingsType bindings_type); ~MojoContextState() override; @@ -61,7 +61,7 @@ const std::vector<std::string>& dependencies) override; // Frame script is executed in. Also used to download resources. - blink::WebFrame* frame_; + blink::WebLocalFrame* frame_; // See description above getter. bool module_added_;
diff --git a/content/renderer/resource_fetcher_browsertest.cc b/content/renderer/resource_fetcher_browsertest.cc index c1d6016..832e6017f 100644 --- a/content/renderer/resource_fetcher_browsertest.cc +++ b/content/renderer/resource_fetcher_browsertest.cc
@@ -26,7 +26,6 @@ #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebView.h" -using blink::WebFrame; using blink::WebURLRequest; using blink::WebURLResponse; @@ -149,7 +148,8 @@ } void ResourceFetcherDownloadOnRenderer(const GURL& url) { - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<FetcherDelegate> delegate(new FetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url)); @@ -165,7 +165,8 @@ } void ResourceFetcher404OnRenderer(const GURL& url) { - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<FetcherDelegate> delegate(new FetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url)); @@ -179,7 +180,8 @@ } void ResourceFetcherDidFailOnRenderer() { - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); // Try to fetch a page on a site that doesn't exist. GURL url("http://localhost:1339/doesnotexist"); @@ -199,7 +201,8 @@ } void ResourceFetcherTimeoutOnRenderer(const GURL& url) { - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<FetcherDelegate> delegate(new FetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url)); @@ -218,7 +221,8 @@ } void ResourceFetcherDeletedInCallbackOnRenderer(const GURL& url) { - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url)); @@ -234,7 +238,8 @@ void ResourceFetcherPost(const GURL& url) { const char* kBody = "Really nifty POST body!"; - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<FetcherDelegate> delegate(new FetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url)); @@ -252,7 +257,8 @@ void ResourceFetcherSetHeader(const GURL& url) { const char* kHeader = "Rather boring header."; - WebFrame* frame = GetRenderView()->GetWebView()->MainFrame(); + blink::WebLocalFrame* frame = + GetRenderView()->GetWebView()->MainFrame()->ToWebLocalFrame(); std::unique_ptr<FetcherDelegate> delegate(new FetcherDelegate); std::unique_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py index a6b5739a..91b5d08 100644 --- a/content/test/gpu/gpu_tests/pixel_expectations.py +++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -67,3 +67,6 @@ ['mac', ('intel', 0x0a2e)], bug=718183) self.Fail('Pixel_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha', ['mac', ('intel', 0x0a2e)], bug=718183) + + self.Flaky('Pixel_OffscreenCanvasTransferBeforeStyleResize', + ['mac', 'linux'], bug=719186)
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h index 6d08ac4..1f2ef7c1 100644 --- a/skia/config/SkUserConfig.h +++ b/skia/config/SkUserConfig.h
@@ -198,10 +198,6 @@ # define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE #endif -#ifndef SK_IGNORE_ETC1_SUPPORT -# define SK_IGNORE_ETC1_SUPPORT -#endif - #ifndef SK_IGNORE_GPU_DITHER # define SK_IGNORE_GPU_DITHER #endif
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 6b001ef..cb4bdcb2 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -285,7 +285,7 @@ "//v8:wasm_fuzzer", ] dict = "dicts/v8_wasm.dict" - seed_corpus = "//v8/test/fuzzer/wasm/" + seed_corpus = "//v8/test/fuzzer/wasm_corpus/" libfuzzer_options = [ "max_len=500" ] } @@ -295,7 +295,7 @@ "//v8:wasm_asmjs_fuzzer", ] dict = "dicts/v8_wasm.dict" - seed_corpus = "//v8/test/fuzzer/wasm_asmjs/" + seed_corpus = "//v8/test/fuzzer/wasm_asmjs_corpus/" libfuzzer_options = [ "max_len=500" ] }
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp index 8207eb6..930cd6f9 100644 --- a/third_party/WebKit/Source/core/editing/Editor.cpp +++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -186,7 +186,7 @@ // |SelectionInDOMTree| instead of |VisibleSelection|. VisibleSelection Editor::SelectionForCommand(Event* event) { VisibleSelection selection = - GetFrame().Selection().ComputeVisibleSelectionInDOMTreeDeprecated(); + GetFrame().Selection().ComputeVisibleSelectionInDOMTree(); if (!event) return selection; // If the target is a text control, and the current selection is outside of @@ -228,12 +228,12 @@ } static bool IsCaretAtStartOfWrappedLine(const FrameSelection& selection) { - if (!selection.ComputeVisibleSelectionInDOMTreeDeprecated().IsCaret()) + if (!selection.ComputeVisibleSelectionInDOMTree().IsCaret()) return false; if (selection.GetSelectionInDOMTree().Affinity() != TextAffinity::kDownstream) return false; const Position& position = - selection.ComputeVisibleSelectionInDOMTreeDeprecated().Start(); + selection.ComputeVisibleSelectionInDOMTree().Start(); return !InSameLine(PositionWithAffinity(position, TextAffinity::kUpstream), PositionWithAffinity(position, TextAffinity::kDownstream)); }
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp index 3ee3ee0..498a59b 100644 --- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp +++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -632,17 +632,16 @@ Node* root = nullptr; Node* select_start_target = nullptr; - if (ComputeVisibleSelectionInDOMTreeDeprecated().IsContentEditable()) { - root = HighestEditableRoot( - ComputeVisibleSelectionInDOMTreeDeprecated().Start()); + if (ComputeVisibleSelectionInDOMTree().IsContentEditable()) { + root = HighestEditableRoot(ComputeVisibleSelectionInDOMTree().Start()); if (Node* shadow_root = NonBoundaryShadowTreeRootNode( - ComputeVisibleSelectionInDOMTreeDeprecated().Start())) + ComputeVisibleSelectionInDOMTree().Start())) select_start_target = shadow_root->OwnerShadowHost(); else select_start_target = root; } else { root = NonBoundaryShadowTreeRootNode( - ComputeVisibleSelectionInDOMTreeDeprecated().Start()); + ComputeVisibleSelectionInDOMTree().Start()); if (root) { select_start_target = root->OwnerShadowHost(); } else { @@ -1107,7 +1106,7 @@ } void FrameSelection::MoveRangeSelectionExtent(const IntPoint& contents_point) { - if (ComputeVisibleSelectionInDOMTreeDeprecated().IsNone()) + if (ComputeVisibleSelectionInDOMTree().IsNone()) return; const SetSelectionOptions kOptions =
diff --git a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp index 67b5b56b..31e6977 100644 --- a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp +++ b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
@@ -261,6 +261,7 @@ TEST_F(FrameSelectionTest, SelectAllWithUnselectableRoot) { Element* select = GetDocument().createElement("select"); GetDocument().ReplaceChild(select, GetDocument().documentElement()); + GetDocument().UpdateStyleAndLayout(); Selection().SelectAll(); EXPECT_TRUE(Selection().ComputeVisibleSelectionInDOMTreeDeprecated().IsNone()) << "Nothing should be selected if the "
diff --git a/third_party/WebKit/Source/core/editing/GranularityStrategy.cpp b/third_party/WebKit/Source/core/editing/GranularityStrategy.cpp index e8bced1a..cb515b81 100644 --- a/third_party/WebKit/Source/core/editing/GranularityStrategy.cpp +++ b/third_party/WebKit/Source/core/editing/GranularityStrategy.cpp
@@ -69,7 +69,7 @@ const VisiblePosition& extent_position = VisiblePositionForContentsPoint(extent_point, frame); const VisibleSelection& selection = - frame->Selection().ComputeVisibleSelectionInDOMTreeDeprecated(); + frame->Selection().ComputeVisibleSelectionInDOMTree(); if (extent_position.IsNull() || selection.VisibleBase().DeepEquivalent() == extent_position.DeepEquivalent()) return selection.AsSelection(); @@ -102,7 +102,7 @@ const IntPoint& extent_point, LocalFrame* frame) { const VisibleSelection& selection = - frame->Selection().ComputeVisibleSelectionInDOMTreeDeprecated(); + frame->Selection().ComputeVisibleSelectionInDOMTree(); if (state_ == StrategyState::kCleared) state_ = StrategyState::kExpanding;
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h index 3e05917..96792811 100644 --- a/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h +++ b/third_party/WebKit/Source/core/frame/WebLocalFrameBase.h
@@ -11,6 +11,10 @@ namespace blink { class LocalFrame; +class Node; +class WebFrameClient; +class WebTextCheckClient; +class WebViewBase; // WebLocalFrameBase is a temporary class the provides a layer of abstraction // for WebLocalFrameImpl. Mehtods that are declared public in WebLocalFrameImpl @@ -25,6 +29,12 @@ CORE_EXPORT static WebLocalFrameBase* FromFrame(LocalFrame*); CORE_EXPORT static WebLocalFrameBase* FromFrame(LocalFrame&); + virtual WebViewBase* ViewImpl() const = 0; + virtual WebFrameClient* Client() const = 0; + virtual WebTextCheckClient* TextCheckClient() const = 0; + virtual void SetContextMenuNode(Node*) = 0; + virtual void ClearContextMenuNode() = 0; + protected: explicit WebLocalFrameBase(WebTreeScopeType scope) : WebLocalFrame(scope) {} };
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp index e0edd169..beb514f 100644 --- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp
@@ -213,8 +213,9 @@ HTMLElement::AttributeChanged(params); } -static bool WasInShadowTreeBeforeInserted(HTMLSlotElement& slot, - ContainerNode& insertion_point) { +static bool WasInDifferentShadowTreeBeforeInserted( + HTMLSlotElement& slot, + ContainerNode& insertion_point) { ShadowRoot* root1 = slot.ContainingShadowRoot(); ShadowRoot* root2 = insertion_point.ContainingShadowRoot(); if (root1 && root2 && root1 == root2) @@ -232,7 +233,8 @@ // Relevant DOM Standard: https://dom.spec.whatwg.org/#concept-node-insert // - 6.4: Run assign slotables for a tree with node's tree and a set // containing each inclusive descendant of node that is a slot. - if (root->IsV1() && !WasInShadowTreeBeforeInserted(*this, *insertion_point)) + if (root->IsV1() && + !WasInDifferentShadowTreeBeforeInserted(*this, *insertion_point)) root->DidAddSlot(*this); }
diff --git a/third_party/WebKit/Source/core/layout/BackgroundBleedAvoidance.h b/third_party/WebKit/Source/core/layout/BackgroundBleedAvoidance.h index 8e35469..2f469081 100644 --- a/third_party/WebKit/Source/core/layout/BackgroundBleedAvoidance.h +++ b/third_party/WebKit/Source/core/layout/BackgroundBleedAvoidance.h
@@ -14,6 +14,11 @@ kBackgroundBleedClipLayer, }; +inline bool BleedAvoidanceIsClipping(BackgroundBleedAvoidance bleed_avoidance) { + return bleed_avoidance == kBackgroundBleedClipOnly || + bleed_avoidance == kBackgroundBleedClipLayer; +} + } // namespace blink #endif // BackgroundBleedAvoidance_h
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp index 9b70c14..9af6417e 100644 --- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
@@ -253,19 +253,8 @@ // we need to clear any override size set previously, so it doesn't // interfere in current layout execution. for (auto* child = FirstInFlowChildBox(); child; - child = child->NextInFlowSiblingBox()) { + child = child->NextInFlowSiblingBox()) child->ClearOverrideSize(); - if (!IsOrthogonalChild(*child) || - (!SizesLogicalWidthToFitContent(StyleRef().LogicalWidth()) && - !StyleRef().LogicalWidth().IsIntrinsicOrAuto())) - continue; - // Additionally, we may need to clear containingBlock override sizes and - // force a layout of the grid items to ensure we get the same result when - // grid's intrinsic size is computed again in the updateLogicalWidth call - // bellow. - child->ClearContainingBlockOverrideSize(); - child->ForceLayout(); - } UpdateLogicalWidth();
diff --git a/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp b/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp index 4e422e72..d5b98c1 100644 --- a/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp +++ b/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp
@@ -334,11 +334,6 @@ } } -bool BleedAvoidanceIsClipping(BackgroundBleedAvoidance bleed_avoidance) { - return bleed_avoidance == kBackgroundBleedClipOnly || - bleed_avoidance == kBackgroundBleedClipLayer; -} - // The LUTs below assume specific enum values. static_assert(kBorderStyleNone == 0, "unexpected EBorderStyle value"); static_assert(kBorderStyleHidden == 1, "unexpected EBorderStyle value");
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp index 7eae8c15..07c01ce 100644 --- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp +++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -100,15 +100,6 @@ return bounds; } -namespace { - -bool BleedAvoidanceIsClipping(BackgroundBleedAvoidance bleed_avoidance) { - return bleed_avoidance == kBackgroundBleedClipOnly || - bleed_avoidance == kBackgroundBleedClipLayer; -} - -} // anonymous namespace - void BoxPainter::PaintBoxDecorationBackgroundWithRect( const PaintInfo& paint_info, const LayoutPoint& paint_offset,
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp index 779c340..6bf37ce7 100644 --- a/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp +++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp
@@ -67,7 +67,6 @@ // Emulates deprecated API use on DedicatedWorkerGlobalScope. void CountDeprecation(UseCounter::Feature feature) { EXPECT_TRUE(IsCurrentThread()); - EXPECT_EQ(0u, GetConsoleMessageStorage()->size()); GlobalScope()->CountDeprecation(feature); // countDeprecation() should add a warning message. @@ -81,6 +80,38 @@ } }; +class InProcessWorkerObjectProxyForTest final + : public InProcessWorkerObjectProxy { + public: + InProcessWorkerObjectProxyForTest( + const WeakPtr<InProcessWorkerMessagingProxy>& messaging_proxy_weak_ptr, + ParentFrameTaskRunners* parent_frame_task_runners) + : InProcessWorkerObjectProxy(messaging_proxy_weak_ptr, + parent_frame_task_runners), + reported_features_(UseCounter::kNumberOfFeatures) { + default_interval_in_sec_ = kDefaultIntervalInSec; + next_interval_in_sec_ = kNextIntervalInSec; + max_interval_in_sec_ = kMaxIntervalInSec; + } + + void CountFeature(UseCounter::Feature feature) override { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + InProcessWorkerObjectProxy::CountFeature(feature); + } + + void CountDeprecation(UseCounter::Feature feature) override { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + InProcessWorkerObjectProxy::CountDeprecation(feature); + } + + private: + BitVector reported_features_; +}; + class InProcessWorkerMessagingProxyForTest : public InProcessWorkerMessagingProxy { public: @@ -88,10 +119,8 @@ : InProcessWorkerMessagingProxy(execution_context, nullptr /* workerObject */, nullptr /* workerClients */) { - WorkerObjectProxy().default_interval_in_sec_ = kDefaultIntervalInSec; - WorkerObjectProxy().next_interval_in_sec_ = kNextIntervalInSec; - WorkerObjectProxy().max_interval_in_sec_ = kMaxIntervalInSec; - + worker_object_proxy_ = WTF::MakeUnique<InProcessWorkerObjectProxyForTest>( + weak_ptr_factory_.CreateWeakPtr(), GetParentFrameTaskRunners()); worker_loader_proxy_provider_ = WTF::MakeUnique<WorkerLoaderProxyProvider>(); worker_thread_ = WTF::WrapUnique(new DedicatedWorkerThreadForTest( @@ -399,6 +428,15 @@ testing::EnterRunLoop(); EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature1)); + // API use should be reported to the Document only one time. See comments in + // InProcessWorkerObjectProxyForTest::CountFeature. + TaskRunnerHelper::Get(TaskType::kUnspecedTimer, GetWorkerThread()) + ->PostTask( + BLINK_FROM_HERE, + CrossThreadBind(&DedicatedWorkerThreadForTest::CountFeature, + CrossThreadUnretained(GetWorkerThread()), kFeature1)); + testing::EnterRunLoop(); + // This feature is randomly selected from Deprecation::deprecationMessage(). const UseCounter::Feature kFeature2 = UseCounter::Feature::kPrefixedStorageInfo; @@ -413,6 +451,15 @@ CrossThreadUnretained(GetWorkerThread()), kFeature2)); testing::EnterRunLoop(); EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature2)); + + // API use should be reported to the Document only one time. See comments in + // InProcessWorkerObjectProxyForTest::CountDeprecation. + TaskRunnerHelper::Get(TaskType::kUnspecedTimer, GetWorkerThread()) + ->PostTask( + BLINK_FROM_HERE, + CrossThreadBind(&DedicatedWorkerThreadForTest::CountDeprecation, + CrossThreadUnretained(GetWorkerThread()), kFeature2)); + testing::EnterRunLoop(); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h index 3dd4b62..60baf1ce 100644 --- a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h +++ b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
@@ -87,7 +87,7 @@ WeakPtr<ThreadedMessagingProxyBase> MessagingProxyWeakPtr() final; private: - friend class InProcessWorkerMessagingProxyForTest; + friend class InProcessWorkerObjectProxyForTest; void StartPendingActivityTimer(); void CheckPendingActivity(TimerBase*);
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp index 0e6ea30e..daaefd8 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp
@@ -26,19 +26,18 @@ MainThreadWorkletGlobalScope::~MainThreadWorkletGlobalScope() {} -void MainThreadWorkletGlobalScope::CountFeature(UseCounter::Feature feature) { +void MainThreadWorkletGlobalScope::ReportFeature(UseCounter::Feature feature) { DCHECK(IsMainThread()); // A parent document is on the same thread, so just record API use in the // document's UseCounter. UseCounter::Count(GetFrame(), feature); } -void MainThreadWorkletGlobalScope::CountDeprecation( +void MainThreadWorkletGlobalScope::ReportDeprecation( UseCounter::Feature feature) { DCHECK(IsMainThread()); // A parent document is on the same thread, so just record API use in the // document's UseCounter. - AddDeprecationMessage(feature); Deprecation::CountDeprecation(GetFrame(), feature); }
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h index f40405b..1fe4a060 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h
@@ -34,8 +34,8 @@ bool IsMainThreadWorkletGlobalScope() const final { return true; } // WorkerOrWorkletGlobalScope - void CountFeature(UseCounter::Feature) final; - void CountDeprecation(UseCounter::Feature) final; + void ReportFeature(UseCounter::Feature) override; + void ReportDeprecation(UseCounter::Feature) override; WorkerThread* GetThread() const final; void FetchAndInvokeScript(const KURL& module_url_record,
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletTest.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorkletTest.cpp index 5fedf9f6..ba8f151 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorkletTest.cpp +++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletTest.cpp
@@ -11,6 +11,39 @@ namespace blink { +class MainThreadWorkletGlobalScopeForTest + : public MainThreadWorkletGlobalScope { + public: + MainThreadWorkletGlobalScopeForTest(LocalFrame* frame, + const KURL& url, + const String& user_agent, + RefPtr<SecurityOrigin> security_origin, + v8::Isolate* isolate) + : MainThreadWorkletGlobalScope(frame, + url, + user_agent, + std::move(security_origin), + isolate), + reported_features_(UseCounter::kNumberOfFeatures) {} + + void ReportFeature(UseCounter::Feature feature) override { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + MainThreadWorkletGlobalScope::ReportFeature(feature); + } + + void ReportDeprecation(UseCounter::Feature feature) final { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + MainThreadWorkletGlobalScope::ReportDeprecation(feature); + } + + private: + BitVector reported_features_; +}; + class MainThreadWorkletTest : public ::testing::Test { public: void SetUp() override { @@ -42,6 +75,10 @@ UseCounter::Count(global_scope_, kFeature1); EXPECT_TRUE(UseCounter::IsCounted(document, kFeature1)); + // API use should be reported to the Document only one time. See comments in + // MainThreadGlobalScopeForTest::ReportFeature. + UseCounter::Count(global_scope_, kFeature1); + // This feature is randomly selected from Deprecation::deprecationMessage(). const UseCounter::Feature kFeature2 = UseCounter::Feature::kPrefixedStorageInfo; @@ -51,6 +88,10 @@ EXPECT_FALSE(UseCounter::IsCounted(document, kFeature2)); Deprecation::CountDeprecation(global_scope_, kFeature2); EXPECT_TRUE(UseCounter::IsCounted(document, kFeature2)); + + // API use should be reported to the Document only one time. See comments in + // MainThreadWorkletGlobalScopeForTest::ReportDeprecation. + Deprecation::CountDeprecation(global_scope_, kFeature2); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp index acd9721..f3a1a74 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp
@@ -30,16 +30,16 @@ DCHECK(!thread_); } -void ThreadedWorkletGlobalScope::CountFeature(UseCounter::Feature feature) { +void ThreadedWorkletGlobalScope::ReportFeature(UseCounter::Feature feature) { DCHECK(IsContextThread()); DCHECK(thread_); thread_->GetWorkerReportingProxy().CountFeature(feature); } -void ThreadedWorkletGlobalScope::CountDeprecation(UseCounter::Feature feature) { +void ThreadedWorkletGlobalScope::ReportDeprecation( + UseCounter::Feature feature) { DCHECK(IsContextThread()); DCHECK(thread_); - AddDeprecationMessage(feature); thread_->GetWorkerReportingProxy().CountDeprecation(feature); }
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h index 5b8795c..81ae12b 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h
@@ -16,8 +16,8 @@ public: ~ThreadedWorkletGlobalScope() override; void Dispose() override; - void CountFeature(UseCounter::Feature) final; - void CountDeprecation(UseCounter::Feature) final; + void ReportFeature(UseCounter::Feature) override; + void ReportDeprecation(UseCounter::Feature) override; // ExecutionContext bool IsThreadedWorkletGlobalScope() const final { return true; }
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h index 7e057206..3e288c7 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h
@@ -32,6 +32,8 @@ } private: + friend class ThreadedWorkletMessagingProxyForTest; + std::unique_ptr<ThreadedWorkletObjectProxy> worklet_object_proxy_; WeakPtrFactory<ThreadedWorkletMessagingProxy> weak_ptr_factory_;
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp index b979310f..e9a2c962 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp
@@ -20,6 +20,35 @@ namespace blink { +class ThreadedWorkletObjectProxyForTest final + : public ThreadedWorkletObjectProxy { + public: + ThreadedWorkletObjectProxyForTest( + const WeakPtr<ThreadedWorkletMessagingProxy>& messaging_proxy_weak_ptr, + ParentFrameTaskRunners* parent_frame_task_runners) + : ThreadedWorkletObjectProxy(messaging_proxy_weak_ptr, + parent_frame_task_runners), + reported_features_(UseCounter::kNumberOfFeatures) {} + + protected: + void CountFeature(UseCounter::Feature feature) override { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + ThreadedWorkletObjectProxy::CountFeature(feature); + } + + void CountDeprecation(UseCounter::Feature feature) final { + // Any feature should be reported only one time. + EXPECT_FALSE(reported_features_.QuickGet(feature)); + reported_features_.QuickSet(feature); + ThreadedWorkletObjectProxy::CountDeprecation(feature); + } + + private: + BitVector reported_features_; +}; + class ThreadedWorkletThreadForTest : public WorkerThread { public: ThreadedWorkletThreadForTest( @@ -27,7 +56,7 @@ WorkerReportingProxy& worker_reporting_proxy) : WorkerThread(WorkerLoaderProxy::Create(worker_loader_proxy_provider), worker_reporting_proxy) {} - ~ThreadedWorkletThreadForTest() override{}; + ~ThreadedWorkletThreadForTest() override {} WorkerBackingThread& GetWorkerBackingThread() override { auto worklet_thread_holder = @@ -72,7 +101,6 @@ // Emulates deprecated API use on ThreadedWorkletGlobalScope. void CountDeprecation(UseCounter::Feature feature) { EXPECT_TRUE(IsCurrentThread()); - EXPECT_EQ(0u, GetConsoleMessageStorage()->size()); GlobalScope()->CountDeprecation(feature); // countDeprecation() should add a warning message. @@ -91,6 +119,8 @@ public: ThreadedWorkletMessagingProxyForTest(ExecutionContext* execution_context) : ThreadedWorkletMessagingProxy(execution_context) { + worklet_object_proxy_ = WTF::MakeUnique<ThreadedWorkletObjectProxyForTest>( + weak_ptr_factory_.CreateWeakPtr(), GetParentFrameTaskRunners()); worker_loader_proxy_provider_ = WTF::MakeUnique<WorkerLoaderProxyProvider>(); worker_thread_ = WTF::MakeUnique<ThreadedWorkletThreadForTest>( @@ -170,7 +200,7 @@ // This feature is randomly selected. const UseCounter::Feature kFeature1 = UseCounter::Feature::kRequestFileSystem; - // API use on the DedicatedWorkerGlobalScope should be recorded in UseCounter + // API use on the ThreadedWorkletGlobalScope should be recorded in UseCounter // on the Document. EXPECT_FALSE(UseCounter::IsCounted(GetDocument(), kFeature1)); TaskRunnerHelper::Get(TaskType::kUnspecedTimer, GetWorkerThread()) @@ -181,6 +211,15 @@ testing::EnterRunLoop(); EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature1)); + // API use should be reported to the Document only one time. See comments in + // ThreadedWorkletGlobalScopeForTest::CountFeature. + TaskRunnerHelper::Get(TaskType::kUnspecedTimer, GetWorkerThread()) + ->PostTask( + BLINK_FROM_HERE, + CrossThreadBind(&ThreadedWorkletThreadForTest::CountFeature, + CrossThreadUnretained(GetWorkerThread()), kFeature1)); + testing::EnterRunLoop(); + // This feature is randomly selected from Deprecation::deprecationMessage(). const UseCounter::Feature kFeature2 = UseCounter::Feature::kPrefixedStorageInfo; @@ -195,6 +234,15 @@ CrossThreadUnretained(GetWorkerThread()), kFeature2)); testing::EnterRunLoop(); EXPECT_TRUE(UseCounter::IsCounted(GetDocument(), kFeature2)); + + // API use should be reported to the Document only one time. See comments in + // ThreadedWorkletGlobalScopeForTest::CountDeprecation. + TaskRunnerHelper::Get(TaskType::kUnspecedTimer, GetWorkerThread()) + ->PostTask( + BLINK_FROM_HERE, + CrossThreadBind(&ThreadedWorkletThreadForTest::CountDeprecation, + CrossThreadUnretained(GetWorkerThread()), kFeature2)); + testing::EnterRunLoop(); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp index a15ff06..a88ef27 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -112,16 +112,15 @@ thread_ = nullptr; } -void WorkerGlobalScope::CountFeature(UseCounter::Feature feature) { +void WorkerGlobalScope::ReportFeature(UseCounter::Feature feature) { DCHECK(IsContextThread()); DCHECK(thread_); thread_->GetWorkerReportingProxy().CountFeature(feature); } -void WorkerGlobalScope::CountDeprecation(UseCounter::Feature feature) { +void WorkerGlobalScope::ReportDeprecation(UseCounter::Feature feature) { DCHECK(IsContextThread()); DCHECK(thread_); - AddDeprecationMessage(feature); thread_->GetWorkerReportingProxy().CountDeprecation(feature); }
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h index e4a9bd8f..03e28e2d 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
@@ -84,8 +84,8 @@ // WorkerOrWorkletGlobalScope bool IsClosing() const final { return closing_; } virtual void Dispose(); - void CountFeature(UseCounter::Feature) final; - void CountDeprecation(UseCounter::Feature) final; + void ReportFeature(UseCounter::Feature) final; + void ReportDeprecation(UseCounter::Feature) final; WorkerThread* GetThread() const final { return thread_; } void ExceptionUnhandled(int exception_id);
diff --git a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp index 322045ff..8743d587 100644 --- a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp
@@ -17,24 +17,33 @@ namespace blink { WorkerOrWorkletGlobalScope::WorkerOrWorkletGlobalScope() - : deprecation_warning_bits_(UseCounter::kNumberOfFeatures) {} + : used_features_(UseCounter::kNumberOfFeatures) {} WorkerOrWorkletGlobalScope::~WorkerOrWorkletGlobalScope() = default; -void WorkerOrWorkletGlobalScope::AddDeprecationMessage( - UseCounter::Feature feature) { +void WorkerOrWorkletGlobalScope::CountFeature(UseCounter::Feature feature) { DCHECK_NE(UseCounter::kOBSOLETE_PageDestruction, feature); DCHECK_GT(UseCounter::kNumberOfFeatures, feature); - - // For each deprecated feature, send console message at most once - // per worker lifecycle. - if (deprecation_warning_bits_.QuickGet(feature)) + if (used_features_.QuickGet(feature)) return; - deprecation_warning_bits_.QuickSet(feature); + used_features_.QuickSet(feature); + ReportFeature(feature); +} + +void WorkerOrWorkletGlobalScope::CountDeprecation(UseCounter::Feature feature) { + DCHECK_NE(UseCounter::kOBSOLETE_PageDestruction, feature); + DCHECK_GT(UseCounter::kNumberOfFeatures, feature); + if (used_features_.QuickGet(feature)) + return; + used_features_.QuickSet(feature); + + // Adds a deprecation message to the console. DCHECK(!Deprecation::DeprecationMessage(feature).IsEmpty()); AddConsoleMessage( ConsoleMessage::Create(kDeprecationMessageSource, kWarningMessageLevel, Deprecation::DeprecationMessage(feature))); + + ReportDeprecation(feature); } void WorkerOrWorkletGlobalScope::PostTask(
diff --git a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h index 8eb68d9..2fd4a65 100644 --- a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h
@@ -41,24 +41,25 @@ virtual void Dispose() = 0; // Called from UseCounter to record API use in this execution context. - virtual void CountFeature(UseCounter::Feature) = 0; + void CountFeature(UseCounter::Feature); // Called from UseCounter to record deprecated API use in this execution - // context. Sub-classes should call addDeprecationMessage() in this function. - virtual void CountDeprecation(UseCounter::Feature) = 0; + // context. + void CountDeprecation(UseCounter::Feature); // May return nullptr if this global scope is not threaded (i.e., // MainThreadWorkletGlobalScope) or after dispose() is called. virtual WorkerThread* GetThread() const = 0; protected: - // Adds a deprecation message to the console. - void AddDeprecationMessage(UseCounter::Feature); + virtual void ReportFeature(UseCounter::Feature) = 0; + virtual void ReportDeprecation(UseCounter::Feature) = 0; private: void RunTask(std::unique_ptr<ExecutionContextTask>, bool is_instrumented); - BitVector deprecation_warning_bits_; + // This is the set of features that this worker has used. + BitVector used_features_; }; DEFINE_TYPE_CASTS(
diff --git a/third_party/WebKit/Source/modules/fetch/BytesConsumer.cpp b/third_party/WebKit/Source/modules/fetch/BytesConsumer.cpp index 7bf2ce96..0c7e7095 100644 --- a/third_party/WebKit/Source/modules/fetch/BytesConsumer.cpp +++ b/third_party/WebKit/Source/modules/fetch/BytesConsumer.cpp
@@ -13,6 +13,7 @@ #include "platform/blob/BlobData.h" #include "platform/wtf/Functional.h" #include "platform/wtf/RefPtr.h" +#include "v8/include/v8.h" namespace blink { @@ -114,6 +115,13 @@ Chunk(const char* data, size_t size) { buffer_.ReserveInitialCapacity(size); buffer_.Append(data, size); + // Report buffer size to V8 so GC can be triggered appropriately. + v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( + static_cast<int64_t>(buffer_.size())); + } + ~Chunk() { + v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( + -static_cast<int64_t>(buffer_.size())); } const char* data() const { return buffer_.data(); } size_t size() const { return buffer_.size(); }
diff --git a/third_party/WebKit/Source/platform/audio/PushPullFIFOMultithreadTest.cpp b/third_party/WebKit/Source/platform/audio/PushPullFIFOMultithreadTest.cpp index 6034d49..fd9efd5 100644 --- a/third_party/WebKit/Source/platform/audio/PushPullFIFOMultithreadTest.cpp +++ b/third_party/WebKit/Source/platform/audio/PushPullFIFOMultithreadTest.cpp
@@ -220,9 +220,6 @@ // Test case 6 (User-specified buffer size): 960 Pull, 128 Push. Minimal // Jitter. 960 frames = 20ms at 48KHz. {48000, 2, 8192, 1000, 960, 1, 128, 1}, - - // Test case 7 (Longer test duration): 256 Pull, 128 Push. 10 seconds. - {48000, 2, 8192, 10000, 256, 0, 128, 1} }; INSTANTIATE_TEST_CASE_P(PushPullFIFOSmokeTest,
diff --git a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp b/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp index c1e00ad..f5151fa2 100644 --- a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp +++ b/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
@@ -44,6 +44,7 @@ #include "core/frame/FrameView.h" #include "core/frame/Settings.h" #include "core/frame/VisualViewport.h" +#include "core/frame/WebLocalFrameBase.h" #include "core/html/HTMLAnchorElement.h" #include "core/html/HTMLFormElement.h" #include "core/html/HTMLImageElement.h" @@ -78,7 +79,6 @@ #include "public/web/WebTextCheckClient.h" #include "public/web/WebViewClient.h" #include "web/ContextMenuAllowedScope.h" -#include "web/WebLocalFrameImpl.h" #include "web/WebPluginContainerImpl.h" namespace blink { @@ -189,8 +189,8 @@ r.SetToShadowHostIfInRestrictedShadowRoot(); LocalFrame* selected_frame = r.InnerNodeFrame(); - WebLocalFrameImpl* selected_web_frame = - WebLocalFrameImpl::FromFrame(selected_frame); + WebLocalFrameBase* selected_web_frame = + WebLocalFrameBase::FromFrame(selected_frame); WebContextMenuData data; data.mouse_position = selected_frame->View()->ContentsToViewport( @@ -420,8 +420,8 @@ if (!selected_frame) return; - WebLocalFrameImpl* selected_web_frame = - WebLocalFrameImpl::FromFrame(selected_frame); + WebLocalFrameBase* selected_web_frame = + WebLocalFrameBase::FromFrame(selected_frame); selected_web_frame->ClearContextMenuNode(); }
diff --git a/third_party/WebKit/Source/web/DedicatedWorkerMessagingProxyProviderImpl.cpp b/third_party/WebKit/Source/web/DedicatedWorkerMessagingProxyProviderImpl.cpp index 8dbb41a..7ba9312 100644 --- a/third_party/WebKit/Source/web/DedicatedWorkerMessagingProxyProviderImpl.cpp +++ b/third_party/WebKit/Source/web/DedicatedWorkerMessagingProxyProviderImpl.cpp
@@ -31,6 +31,7 @@ #include "web/DedicatedWorkerMessagingProxyProviderImpl.h" #include "core/dom/Document.h" +#include "core/frame/WebLocalFrameBase.h" #include "core/loader/WorkerFetchContext.h" #include "core/workers/DedicatedWorkerMessagingProxy.h" #include "core/workers/Worker.h" @@ -46,7 +47,6 @@ #include "public/web/WebWorkerContentSettingsClientProxy.h" #include "web/IndexedDBClientImpl.h" #include "web/LocalFileSystemClient.h" -#include "web/WebLocalFrameImpl.h" namespace blink { @@ -59,8 +59,8 @@ Worker* worker) { if (worker->GetExecutionContext()->IsDocument()) { Document* document = ToDocument(worker->GetExecutionContext()); - WebLocalFrameImpl* web_frame = - WebLocalFrameImpl::FromFrame(document->GetFrame()); + WebLocalFrameBase* web_frame = + WebLocalFrameBase::FromFrame(document->GetFrame()); WorkerClients* worker_clients = WorkerClients::Create(); ProvideIndexedDBClientToWorker( worker_clients, IndexedDBClientImpl::Create(*worker_clients));
diff --git a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp index 53eb4fc..a681c4a 100644 --- a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp +++ b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
@@ -10,6 +10,7 @@ #include "core/events/WheelEvent.h" #include "core/frame/RemoteFrame.h" #include "core/frame/RemoteFrameView.h" +#include "core/frame/WebLocalFrameBase.h" #include "core/layout/api/LayoutItem.h" #include "core/layout/api/LayoutPartItem.h" #include "platform/exported/WrappedResourceRequest.h" @@ -19,7 +20,6 @@ #include "platform/wtf/PtrUtil.h" #include "public/web/WebRemoteFrameClient.h" #include "web/WebInputEventConversion.h" -#include "web/WebLocalFrameImpl.h" #include "web/WebRemoteFrameImpl.h" namespace blink { @@ -132,7 +132,7 @@ LocalFrame* source_frame) const { if (web_frame_->Client()) web_frame_->Client()->ForwardPostMessage( - WebLocalFrameImpl::FromFrame(source_frame), web_frame_, + WebLocalFrameBase::FromFrame(source_frame), web_frame_, WebSecurityOrigin(std::move(target)), WebDOMMessageEvent(event)); } @@ -148,7 +148,7 @@ void RemoteFrameClientImpl::AdvanceFocus(WebFocusType type, LocalFrame* source) { web_frame_->Client()->AdvanceFocus(type, - WebLocalFrameImpl::FromFrame(source)); + WebLocalFrameBase::FromFrame(source)); } void RemoteFrameClientImpl::VisibilityChanged(bool visible) {
diff --git a/third_party/WebKit/Source/web/WebAXObject.cpp b/third_party/WebKit/Source/web/WebAXObject.cpp index bef659e..a9efc6c 100644 --- a/third_party/WebKit/Source/web/WebAXObject.cpp +++ b/third_party/WebKit/Source/web/WebAXObject.cpp
@@ -39,6 +39,7 @@ #include "core/exported/WebViewBase.h" #include "core/frame/FrameView.h" #include "core/frame/VisualViewport.h" +#include "core/frame/WebLocalFrameBase.h" #include "core/input/KeyboardEventManager.h" #include "core/page/Page.h" #include "core/style/ComputedStyle.h" @@ -57,7 +58,6 @@ #include "public/web/WebDocument.h" #include "public/web/WebElement.h" #include "public/web/WebNode.h" -#include "web/WebLocalFrameImpl.h" namespace blink { @@ -944,7 +944,7 @@ if (!frame) return; - WebViewBase* view = WebLocalFrameImpl::FromFrame(frame)->ViewImpl(); + WebViewBase* view = WebLocalFrameBase::FromFrame(frame)->ViewImpl(); if (!view) return;
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.h b/third_party/WebKit/Source/web/WebLocalFrameImpl.h index b7d6844..c4973398 100644 --- a/third_party/WebKit/Source/web/WebLocalFrameImpl.h +++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
@@ -360,7 +360,7 @@ static WebPluginContainerImpl* CurrentPluginContainer(LocalFrame*, Node* = nullptr); - WebViewBase* ViewImpl() const; + WebViewBase* ViewImpl() const override; FrameView* GetFrameView() const { return GetFrame() ? GetFrame()->View() : 0; @@ -391,7 +391,7 @@ // Otherwise, disallow scrolling. void SetCanHaveScrollbars(bool) override; - WebFrameClient* Client() const { return client_; } + WebFrameClient* Client() const override { return client_; } void SetClient(WebFrameClient* client) { client_ = client; } ContentSettingsClient& GetContentSettingsClient() { @@ -407,7 +407,9 @@ static void SelectWordAroundPosition(LocalFrame*, VisiblePosition); TextCheckerClient& GetTextCheckerClient() const; - WebTextCheckClient* TextCheckClient() const { return text_check_client_; } + WebTextCheckClient* TextCheckClient() const override { + return text_check_client_; + } TextFinder* GetTextFinder() const; // Returns the text finder object if it already exists. @@ -428,8 +430,8 @@ } WebNode ContextMenuNode() const { return context_menu_node_.Get(); } - void SetContextMenuNode(Node* node) { context_menu_node_ = node; } - void ClearContextMenuNode() { context_menu_node_.Clear(); } + void SetContextMenuNode(Node* node) override { context_menu_node_ = node; } + void ClearContextMenuNode() override { context_menu_node_.Clear(); } DECLARE_TRACE();
diff --git a/third_party/WebKit/Source/web/WebNode.cpp b/third_party/WebKit/Source/web/WebNode.cpp index 9ea216f1..390399c 100644 --- a/third_party/WebKit/Source/web/WebNode.cpp +++ b/third_party/WebKit/Source/web/WebNode.cpp
@@ -56,8 +56,6 @@ #include "public/web/WebElement.h" #include "public/web/WebElementCollection.h" #include "public/web/WebPluginContainer.h" -#include "web/LocalFrameClientImpl.h" -#include "web/WebLocalFrameImpl.h" #include "web/WebPluginContainerImpl.h" namespace blink {
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath index 69a0a6f8..871da5e2 100644 --- a/tools/android/eclipse/.classpath +++ b/tools/android/eclipse/.classpath
@@ -215,6 +215,7 @@ <classpathentry kind="src" path="out/Debug/java_mojo/mojo_public_test_interfaces_mojom/src"/> <classpathentry kind="src" path="out/Debug/java_proto/cacheinvalidation_proto_java/src"/> <classpathentry kind="src" path="out/Debug/java_proto/document_tab_model_info_proto_java/src"/> + <classpathentry kind="src" path="out/Debug/java_proto/partner_location_descriptor_proto_java/src"/> <classpathentry kind="src" path="out/Debug/java_proto/test_support_proto_java/src"/> <classpathentry kind="src" path="out/Debug/remoting_apk/gen"/> <classpathentry kind="lib" path="third_party/android_tools/sdk/extras/google/gcm/gcm-client/dist/gcm.jar" sourcepath="third_party/android_tools/sdk/extras/google/gcm/gcm-client/src"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 6968841..7baf69d 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -21889,7 +21889,9 @@ <int value="1108663108" label="disable-device-discovery-notifications"/> <int value="1113365156" label="tab-management-experiment-type-chive"/> <int value="1114629582" label="enable-floating-virtual-keyboard"/> + <int value="1116593018" label="CaptureThumbnailOnLoadFinished:disabled"/> <int value="1118109174" label="enable-launcher-search-provider-api"/> + <int value="1126061778" label="CaptureThumbnailOnLoadFinished:enabled"/> <int value="1127183523" label="PassiveEventListenersDueToFling:enabled"/> <int value="1127427821" label="OmniboxEntitySuggestions:disabled"/> <int value="1129888794" label="ash-touch-hud"/>
diff --git a/ui/message_center/views/message_list_view.cc b/ui/message_center/views/message_list_view.cc index 28cb247..83c969d 100644 --- a/ui/message_center/views/message_list_view.cc +++ b/ui/message_center/views/message_list_view.cc
@@ -560,6 +560,9 @@ new_bounds.set_x(new_bounds.right() + kMarginBetweenItems); animator_.AnimateViewTo(child, new_bounds); + // Deleting the child after animation. + deleted_when_done_.insert(child); + // Schedule to start sliding out next notification after a short delay. if (!clearing_all_views_.empty()) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
diff --git a/ui/message_center/views/message_list_view_unittest.cc b/ui/message_center/views/message_list_view_unittest.cc index 8d139a8..3afd6e7 100644 --- a/ui/message_center/views/message_list_view_unittest.cc +++ b/ui/message_center/views/message_list_view_unittest.cc
@@ -59,7 +59,11 @@ MockNotificationView::MockNotificationView(MessageCenterController* controller, const Notification& notification, Test* test) - : NotificationView(controller, notification), test_(test) {} + : NotificationView(controller, notification), test_(test) { + // Calling SetPaintToLayer() to ensure that this view has its own layer. + // This layer is needed to enable adding/removal animations. + SetPaintToLayer(); +} MockNotificationView::~MockNotificationView() {} @@ -441,15 +445,48 @@ RunPendingAnimations(); - // TODO(yhanada): notification_view1 and notification_view2 should be deleted - // here. Uncomment the below test. - EXPECT_TRUE(gfx::IntersectRects(notification_view1->bounds(), - message_list_view()->bounds()) - .IsEmpty()); - EXPECT_TRUE(gfx::IntersectRects(notification_view2->bounds(), - message_list_view()->bounds()) - .IsEmpty()); - // EXPECT_EQ(0, message_list_view()->child_count()); + EXPECT_EQ(0, message_list_view()->child_count()); +} + +// Regression test for crbug.com/713983 +TEST_F(MessageListViewTest, RemoveWhileClearAll) { + message_list_view()->SetBounds(0, 0, 800, 600); + + // Create dummy notifications. + auto* notification_view1 = CreateNotificationView( + Notification(NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId1), + base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message1"), + gfx::Image(), base::UTF8ToUTF16("display source"), GURL(), + NotifierId(NotifierId::APPLICATION, "extension_id"), + message_center::RichNotificationData(), nullptr)); + auto* notification_view2 = CreateNotificationView( + Notification(NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId2), + base::UTF8ToUTF16("title 2"), base::UTF8ToUTF16("message2"), + gfx::Image(), base::UTF8ToUTF16("display source"), GURL(), + NotifierId(NotifierId::APPLICATION, "extension_id"), + message_center::RichNotificationData(), nullptr)); + + message_list_view()->AddNotificationAt(notification_view1, 0); + EXPECT_EQ(1, message_list_view()->child_count()); + + RunPendingAnimations(); + + message_list_view()->AddNotificationAt(notification_view2, 1); + EXPECT_EQ(2, message_list_view()->child_count()); + + RunPendingAnimations(); + + // Call RemoveNotification() + EXPECT_TRUE(message_list_view()->Contains(notification_view2)); + message_list_view()->RemoveNotification(notification_view2); + + // Call "Clear All" while notification_view2 is still in message_list_view. + EXPECT_TRUE(message_list_view()->Contains(notification_view2)); + message_list_view()->ClearAllClosableNotifications( + message_list_view()->bounds()); + + RunPendingAnimations(); + EXPECT_EQ(0, message_list_view()->child_count()); } } // namespace