text:
namespace cc {
namespace {

SurfaceId InvalidSurfaceId() {
  static SurfaceId invalid;
  invalid.id = static_cast<uint64_t>(-1);
  return invalid;
}

gfx::Size SurfaceSize() {
  static gfx::Size size(5, 5);
  return size;
}

class EmptySurfaceFactoryClient : public SurfaceFactoryClient {
 public:
  void ReturnResources(const ReturnedResourceArray& resources) override {}

  void SetBeginFrameSource(SurfaceId surface_id,
                           BeginFrameSource* begin_frame_source) override {}
};

class FakeSurfaceAggregatorClient : public SurfaceAggregatorClient {
 public:
  void AddSurface(Surface* surface) override {
    EXPECT_FALSE(HasSurface(surface));
    surfaces_.insert(surface);
  }

  void RemoveSurface(Surface* surface) override {
    EXPECT_TRUE(HasSurface(surface));
    surfaces_.erase(surface);
  }

  bool HasSurface(Surface* surface) const {
    return surfaces_.count(surface) != 0;
  }

 private:
  std::set<Surface*> surfaces_;
};

class SurfaceAggregatorTest : public testing::Test {
 public:
  explicit SurfaceAggregatorTest(bool use_damage_rect)
      : factory_(&manager_, &empty_client_),
        aggregator_(&surface_aggregator_client_,
                    &manager_,
                    NULL,
                    use_damage_rect) {}

  SurfaceAggregatorTest() : SurfaceAggregatorTest(false) {}

 protected:
  SurfaceManager manager_;
  EmptySurfaceFactoryClient empty_client_;
  SurfaceFactory factory_;
  FakeSurfaceAggregatorClient surface_aggregator_client_;
  SurfaceAggregator aggregator_;
};

TEST_F(SurfaceAggregatorTest, ValidSurfaceNoFrame) {
  SurfaceId one_id(7);
  factory_.Create(one_id);
  Surface* surface = manager_.GetSurfaceForId(one_id);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface));
  scoped_ptr<CompositorFrame> frame = aggregator_.Aggregate(one_id);
  EXPECT_FALSE(frame);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface));

  factory_.Destroy(one_id);
}

class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
 public:
  explicit SurfaceAggregatorValidSurfaceTest(bool use_damage_rect)
      : SurfaceAggregatorTest(use_damage_rect),
        allocator_(1u),
        child_allocator_(2u) {}
  SurfaceAggregatorValidSurfaceTest()
      : SurfaceAggregatorValidSurfaceTest(false) {}

  void SetUp() override {
    SurfaceAggregatorTest::SetUp();
    root_surface_id_ = allocator_.GenerateId();
    factory_.Create(root_surface_id_);
    root_surface_ = manager_.GetSurfaceForId(root_surface_id_);
  }

  void TearDown() override {
    factory_.Destroy(root_surface_id_);
    SurfaceAggregatorTest::TearDown();
  }

  void AggregateAndVerify(test::Pass* expected_passes,
                          size_t expected_pass_count,
                          SurfaceId* surface_ids,
                          size_t expected_surface_count) {
    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    TestPassesMatchExpectations(
        expected_passes, expected_pass_count, &frame_data->render_pass_list);

    // Ensure no duplicate pass ids output.
    std::set<RenderPassId> used_passes;
    for (auto* pass : frame_data->render_pass_list) {
      EXPECT_TRUE(used_passes.insert(pass->id).second);
    }

    EXPECT_EQ(expected_surface_count,
              aggregator_.previous_contained_surfaces().size());
    for (size_t i = 0; i < expected_surface_count; i++) {
      EXPECT_TRUE(
          aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
          aggregator_.previous_contained_surfaces().end());
    }
  }

  void SubmitPassListAsFrame(SurfaceId surface_id, RenderPassList* pass_list) {
    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
    pass_list->swap(frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
    frame->delegated_frame_data = frame_data.Pass();

    factory_.SubmitCompositorFrame(surface_id, frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

  void SubmitCompositorFrame(test::Pass* passes,
                             size_t pass_count,
                             SurfaceId surface_id) {
    RenderPassList pass_list;
    AddPasses(&pass_list, gfx::Rect(SurfaceSize()), passes, pass_count);
    SubmitPassListAsFrame(surface_id, &pass_list);
  }

  void QueuePassAsFrame(scoped_ptr<RenderPass> pass, SurfaceId surface_id) {
    scoped_ptr<DelegatedFrameData> delegated_frame_data(new DelegatedFrameData);
    delegated_frame_data->render_pass_list.push_back(pass.Pass());

    scoped_ptr<CompositorFrame> child_frame(new CompositorFrame);
    child_frame->delegated_frame_data = delegated_frame_data.Pass();

    factory_.SubmitCompositorFrame(surface_id, child_frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

 protected:
  SurfaceId root_surface_id_;
  Surface* root_surface_;
  SurfaceIdAllocator allocator_;
  SurfaceIdAllocator child_allocator_;
};

// Tests that a very simple frame containing only two solid color quads makes it
// through the aggregator correctly.
TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
  test::Quad quads[] = {test::Quad::SolidColorQuad(SK_ColorRED),
                        test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass passes[] = {test::Pass(quads, arraysize(quads))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  SurfaceId ids[] = {root_surface_id_};

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
}

TEST_F(SurfaceAggregatorValidSurfaceTest, OpacityCopied) {
  SurfaceId embedded_surface_id = allocator_.GenerateId();
  factory_.Create(embedded_surface_id);
  Surface* embedded_surface = manager_.GetSurfaceForId(embedded_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  test::Quad embedded_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass embedded_passes[] = {
      test::Pass(embedded_quads, arraysize(embedded_quads))};

  SubmitCompositorFrame(embedded_passes, arraysize(embedded_passes),
                        embedded_surface_id);

  test::Quad quads[] = {test::Quad::SurfaceQuad(embedded_surface_id, .5f)};
  test::Pass passes[] = {test::Pass(quads, arraysize(quads))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(embedded_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  RenderPassList& render_pass_list(frame_data->render_pass_list);
  ASSERT_EQ(2u, render_pass_list.size());
  SharedQuadStateList& shared_quad_state_list(
      render_pass_list[0]->shared_quad_state_list);
  ASSERT_EQ(2u, shared_quad_state_list.size());
  EXPECT_EQ(1.f, shared_quad_state_list.ElementAt(0)->opacity);
  EXPECT_EQ(1.f, shared_quad_state_list.ElementAt(1)->opacity);

  SharedQuadStateList& shared_quad_state_list2(
      render_pass_list[1]->shared_quad_state_list);
  ASSERT_EQ(1u, shared_quad_state_list2.size());
  EXPECT_EQ(.5f, shared_quad_state_list2.ElementAt(0)->opacity);

  factory_.Destroy(embedded_surface_id);
}

TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) {
  test::Quad quads[][2] = {{test::Quad::SolidColorQuad(SK_ColorWHITE),
                            test::Quad::SolidColorQuad(SK_ColorLTGRAY)},
                           {test::Quad::SolidColorQuad(SK_ColorGRAY),
                            test::Quad::SolidColorQuad(SK_ColorDKGRAY)}};
  test::Pass passes[] = {
      test::Pass(quads[0], arraysize(quads[0]), RenderPassId(1, 1)),
      test::Pass(quads[1], arraysize(quads[1]), RenderPassId(1, 2))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  SurfaceId ids[] = {root_surface_id_};

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
}

// This tests very simple embedding. root_surface has a frame containing a few
// solid color quads and a surface quad referencing embedded_surface.
// embedded_surface has a frame containing only a solid color quad. The solid
// color quad should be aggregated into the final frame.
TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
  SurfaceId embedded_surface_id = allocator_.GenerateId();
  factory_.Create(embedded_surface_id);
  Surface* embedded_surface = manager_.GetSurfaceForId(embedded_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  test::Quad embedded_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN)};
  test::Pass embedded_passes[] = {
      test::Pass(embedded_quads, arraysize(embedded_quads))};

  SubmitCompositorFrame(embedded_passes, arraysize(embedded_passes),
                        embedded_surface_id);

  test::Quad root_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE),
                             test::Quad::SurfaceQuad(embedded_surface_id, 1.f),
                             test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))};

  SubmitCompositorFrame(root_passes, arraysize(root_passes), root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE),
                                 test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads))};
  SurfaceId ids[] = {root_surface_id_, embedded_surface_id};
  AggregateAndVerify(
      expected_passes, arraysize(expected_passes), ids, arraysize(ids));

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(embedded_surface));

  factory_.Destroy(embedded_surface_id);
}

TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
  SurfaceId embedded_surface_id = allocator_.GenerateId();
  factory_.Create(embedded_surface_id);
  Surface* embedded_surface = manager_.GetSurfaceForId(embedded_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  test::Quad embedded_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN)};
  test::Pass embedded_passes[] = {
      test::Pass(embedded_quads, arraysize(embedded_quads))};

  SubmitCompositorFrame(embedded_passes, arraysize(embedded_passes),
                        embedded_surface_id);
  scoped_ptr<CopyOutputRequest> copy_request(
      CopyOutputRequest::CreateEmptyRequest());
  CopyOutputRequest* copy_request_ptr = copy_request.get();
  factory_.RequestCopyOfSurface(embedded_surface_id, copy_request.Pass());

  test::Quad root_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE),
                             test::Quad::SurfaceQuad(embedded_surface_id, 1.f),
                             test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))};

  SubmitCompositorFrame(root_passes, arraysize(root_passes), root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(embedded_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  test::Quad expected_quads[] = {
      test::Quad::SolidColorQuad(SK_ColorWHITE),
      test::Quad::RenderPassQuad(frame_data->render_pass_list[0]->id),
      test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Pass expected_passes[] = {
      test::Pass(embedded_quads, arraysize(embedded_quads)),
      test::Pass(expected_quads, arraysize(expected_quads))};
  TestPassesMatchExpectations(expected_passes,
                              arraysize(expected_passes),
                              &frame_data->render_pass_list);
  ASSERT_EQ(2u, frame_data->render_pass_list.size());
  ASSERT_EQ(1u, frame_data->render_pass_list[0]->copy_requests.size());
  DCHECK_EQ(copy_request_ptr,
            frame_data->render_pass_list[0]->copy_requests[0]);

  SurfaceId surface_ids[] = {root_surface_id_, embedded_surface_id};
  EXPECT_EQ(arraysize(surface_ids),
            aggregator_.previous_contained_surfaces().size());
  for (size_t i = 0; i < arraysize(surface_ids); i++) {
    EXPECT_TRUE(
        aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
        aggregator_.previous_contained_surfaces().end());
  }

  factory_.Destroy(embedded_surface_id);
}

// Root surface may contain copy requests.
TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
  SurfaceId embedded_surface_id = allocator_.GenerateId();
  factory_.Create(embedded_surface_id);
  Surface* embedded_surface = manager_.GetSurfaceForId(embedded_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  test::Quad embedded_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN)};
  test::Pass embedded_passes[] = {
      test::Pass(embedded_quads, arraysize(embedded_quads))};

  SubmitCompositorFrame(embedded_passes, arraysize(embedded_passes),
                        embedded_surface_id);
  scoped_ptr<CopyOutputRequest> copy_request(
      CopyOutputRequest::CreateEmptyRequest());
  CopyOutputRequest* copy_request_ptr = copy_request.get();
  scoped_ptr<CopyOutputRequest> copy_request2(
      CopyOutputRequest::CreateEmptyRequest());
  CopyOutputRequest* copy_request2_ptr = copy_request2.get();

  test::Quad root_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE),
                             test::Quad::SurfaceQuad(embedded_surface_id, 1.f),
                             test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Quad root_quads2[] = {test::Quad::SolidColorQuad(SK_ColorRED)};
  test::Pass root_passes[] = {
      test::Pass(root_quads, arraysize(root_quads), RenderPassId(1, 1)),
      test::Pass(root_quads2, arraysize(root_quads2), RenderPassId(1, 2))};
  {
    RenderPassList pass_list;
    AddPasses(&pass_list,
              gfx::Rect(SurfaceSize()),
              root_passes,
              arraysize(root_passes));
    pass_list[0]->copy_requests.push_back(copy_request.Pass());
    pass_list[1]->copy_requests.push_back(copy_request2.Pass());

    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
    pass_list.swap(frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
    frame->delegated_frame_data = frame_data.Pass();

    factory_.SubmitCompositorFrame(root_surface_id_, frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(embedded_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorWHITE),
                                 test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorBLACK)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads)),
      test::Pass(root_quads2, arraysize(root_quads2))};
  TestPassesMatchExpectations(expected_passes,
                              arraysize(expected_passes),
                              &frame_data->render_pass_list);
  ASSERT_EQ(2u, frame_data->render_pass_list.size());
  ASSERT_EQ(1u, frame_data->render_pass_list[0]->copy_requests.size());
  DCHECK_EQ(copy_request_ptr,
            frame_data->render_pass_list[0]->copy_requests[0]);
  ASSERT_EQ(1u, frame_data->render_pass_list[1]->copy_requests.size());
  DCHECK_EQ(copy_request2_ptr,
            frame_data->render_pass_list[1]->copy_requests[0]);

  SurfaceId surface_ids[] = {root_surface_id_, embedded_surface_id};
  EXPECT_EQ(arraysize(surface_ids),
            aggregator_.previous_contained_surfaces().size());
  for (size_t i = 0; i < arraysize(surface_ids); i++) {
    EXPECT_TRUE(
        aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
        aggregator_.previous_contained_surfaces().end());
  }

  // Ensure copy requests have been removed from root surface.
  const CompositorFrame* original_frame =
      manager_.GetSurfaceForId(root_surface_id_)->GetEligibleFrame();
  RenderPassList& original_pass_list =
      original_frame->delegated_frame_data->render_pass_list;
  ASSERT_EQ(2u, original_pass_list.size());
  DCHECK(original_pass_list[0]->copy_requests.empty());
  DCHECK(original_pass_list[1]->copy_requests.empty());

  factory_.Destroy(embedded_surface_id);
}

// This tests referencing a surface that has multiple render passes.
TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) {
  SurfaceId embedded_surface_id = child_allocator_.GenerateId();
  factory_.Create(embedded_surface_id);
  Surface* embedded_surface = manager_.GetSurfaceForId(embedded_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  RenderPassId pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2),
                             RenderPassId(1, 3)};

  test::Quad embedded_quads[][2] = {
      {test::Quad::SolidColorQuad(1), test::Quad::SolidColorQuad(2)},
      {test::Quad::SolidColorQuad(3), test::Quad::RenderPassQuad(pass_ids[0])},
      {test::Quad::SolidColorQuad(4), test::Quad::RenderPassQuad(pass_ids[1])}};
  test::Pass embedded_passes[] = {
      test::Pass(embedded_quads[0], arraysize(embedded_quads[0]), pass_ids[0]),
      test::Pass(embedded_quads[1], arraysize(embedded_quads[1]), pass_ids[1]),
      test::Pass(embedded_quads[2], arraysize(embedded_quads[2]), pass_ids[2])};

  SubmitCompositorFrame(embedded_passes, arraysize(embedded_passes),
                        embedded_surface_id);

  test::Quad root_quads[][2] = {
      {test::Quad::SolidColorQuad(5), test::Quad::SolidColorQuad(6)},
      {test::Quad::SurfaceQuad(embedded_surface_id, 1.f),
       test::Quad::RenderPassQuad(pass_ids[0])},
      {test::Quad::SolidColorQuad(7), test::Quad::RenderPassQuad(pass_ids[1])}};
  test::Pass root_passes[] = {
      test::Pass(root_quads[0], arraysize(root_quads[0]), pass_ids[0]),
      test::Pass(root_quads[1], arraysize(root_quads[1]), pass_ids[1]),
      test::Pass(root_quads[2], arraysize(root_quads[2]), pass_ids[2])};

  SubmitCompositorFrame(root_passes, arraysize(root_passes), root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(embedded_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(embedded_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(5u, aggregated_pass_list.size());
  RenderPassId actual_pass_ids[] = {
      aggregated_pass_list[0]->id, aggregated_pass_list[1]->id,
      aggregated_pass_list[2]->id, aggregated_pass_list[3]->id,
      aggregated_pass_list[4]->id};
  for (size_t i = 0; i < 5; ++i) {
    for (size_t j = 0; j < i; ++j) {
      EXPECT_NE(actual_pass_ids[i], actual_pass_ids[j]);
    }
  }

  {
    SCOPED_TRACE("First pass");
    // The first pass will just be the first pass from the root surfaces quad
    // with no render pass quads to remap.
    TestPassMatchesExpectations(root_passes[0], aggregated_pass_list[0]);
  }

  {
    SCOPED_TRACE("Second pass");
    // The next two passes will be from the embedded surface since we have to
    // draw those passes before they are referenced from the render pass draw
    // quad embedded into the root surface's second pass.
    // First, there's the first embedded pass which doesn't reference anything
    // else.
    TestPassMatchesExpectations(embedded_passes[0], aggregated_pass_list[1]);
  }

  {
    SCOPED_TRACE("Third pass");
    const QuadList& third_pass_quad_list = aggregated_pass_list[2]->quad_list;
    ASSERT_EQ(2u, third_pass_quad_list.size());
    TestQuadMatchesExpectations(embedded_quads[1][0],
                                third_pass_quad_list.ElementAt(0));

    // This render pass pass quad will reference the first pass from the
    // embedded surface, which is the second pass in the aggregated frame.
    ASSERT_EQ(DrawQuad::RENDER_PASS,
              third_pass_quad_list.ElementAt(1)->material);
    const RenderPassDrawQuad* third_pass_render_pass_draw_quad =
        RenderPassDrawQuad::MaterialCast(third_pass_quad_list.ElementAt(1));
    EXPECT_EQ(actual_pass_ids[1],
              third_pass_render_pass_draw_quad->render_pass_id);
  }

  {
    SCOPED_TRACE("Fourth pass");
    // The fourth pass will have aggregated quads from the root surface's second
    // pass and the embedded surface's first pass.
    const QuadList& fourth_pass_quad_list = aggregated_pass_list[3]->quad_list;
    ASSERT_EQ(3u, fourth_pass_quad_list.size());

    // The first quad will be the yellow quad from the embedded surface's last
    // pass.
    TestQuadMatchesExpectations(embedded_quads[2][0],
                                fourth_pass_quad_list.ElementAt(0));

    // The next quad will be a render pass quad referencing the second pass from
    // the embedded surface, which is the third pass in the aggregated frame.
    ASSERT_EQ(DrawQuad::RENDER_PASS,
              fourth_pass_quad_list.ElementAt(1)->material);
    const RenderPassDrawQuad* fourth_pass_first_render_pass_draw_quad =
        RenderPassDrawQuad::MaterialCast(fourth_pass_quad_list.ElementAt(1));
    EXPECT_EQ(actual_pass_ids[2],
              fourth_pass_first_render_pass_draw_quad->render_pass_id);

    // The last quad will be a render pass quad referencing the first pass from
    // the root surface, which is the first pass overall.
    ASSERT_EQ(DrawQuad::RENDER_PASS,
              fourth_pass_quad_list.ElementAt(2)->material);
    const RenderPassDrawQuad* fourth_pass_second_render_pass_draw_quad =
        RenderPassDrawQuad::MaterialCast(fourth_pass_quad_list.ElementAt(2));
    EXPECT_EQ(actual_pass_ids[0],
              fourth_pass_second_render_pass_draw_quad->render_pass_id);
  }

  {
    SCOPED_TRACE("Fifth pass");
    const QuadList& fifth_pass_quad_list = aggregated_pass_list[4]->quad_list;
    ASSERT_EQ(2u, fifth_pass_quad_list.size());

    TestQuadMatchesExpectations(root_quads[2][0],
                                fifth_pass_quad_list.ElementAt(0));

    // The last quad in the last pass will reference the second pass from the
    // root surface, which after aggregating is the fourth pass in the overall
    // list.
    ASSERT_EQ(DrawQuad::RENDER_PASS,
              fifth_pass_quad_list.ElementAt(1)->material);
    const RenderPassDrawQuad* fifth_pass_render_pass_draw_quad =
        RenderPassDrawQuad::MaterialCast(fifth_pass_quad_list.ElementAt(1));
    EXPECT_EQ(actual_pass_ids[3],
              fifth_pass_render_pass_draw_quad->render_pass_id);
  }
  factory_.Destroy(embedded_surface_id);
}

// Tests an invalid surface reference in a frame. The surface quad should just
// be dropped.
TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) {
  test::Quad quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                        test::Quad::SurfaceQuad(InvalidSurfaceId(), 1.f),
                        test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass passes[] = {test::Pass(quads, arraysize(quads))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads))};
  SurfaceId ids[] = {root_surface_id_, InvalidSurfaceId()};

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  AggregateAndVerify(
      expected_passes, arraysize(expected_passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
}

// Tests a reference to a valid surface with no submitted frame. This quad
// should also just be dropped.
TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) {
  SurfaceId surface_with_no_frame_id = allocator_.GenerateId();
  factory_.Create(surface_with_no_frame_id);
  Surface* surface_with_no_frame =
      manager_.GetSurfaceForId(surface_with_no_frame_id);

  test::Quad quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                        test::Quad::SurfaceQuad(surface_with_no_frame_id, 1.f),
                        test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass passes[] = {test::Pass(quads, arraysize(quads))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorBLUE)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads))};
  SurfaceId ids[] = {root_surface_id_, surface_with_no_frame_id};
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface_with_no_frame));
  AggregateAndVerify(
      expected_passes, arraysize(expected_passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface_with_no_frame));
  factory_.Destroy(surface_with_no_frame_id);
}

// Tests a surface quad referencing itself, generating a trivial cycle.
// The quad creating the cycle should be dropped from the final frame.
TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) {
  test::Quad quads[] = {test::Quad::SurfaceQuad(root_surface_id_, 1.f),
                        test::Quad::SolidColorQuad(SK_ColorYELLOW)};
  test::Pass passes[] = {test::Pass(quads, arraysize(quads))};

  SubmitCompositorFrame(passes, arraysize(passes), root_surface_id_);

  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorYELLOW)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads))};
  SurfaceId ids[] = {root_surface_id_};
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  AggregateAndVerify(
      expected_passes, arraysize(expected_passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
}

// Tests a more complex cycle with one intermediate surface.
TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
  SurfaceId child_surface_id = allocator_.GenerateId();
  factory_.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  test::Quad parent_quads[] = {test::Quad::SolidColorQuad(SK_ColorBLUE),
                               test::Quad::SurfaceQuad(child_surface_id, 1.f),
                               test::Quad::SolidColorQuad(SK_ColorCYAN)};
  test::Pass parent_passes[] = {
      test::Pass(parent_quads, arraysize(parent_quads))};

  SubmitCompositorFrame(parent_passes, arraysize(parent_passes),
                        root_surface_id_);

  test::Quad child_quads[] = {test::Quad::SolidColorQuad(SK_ColorGREEN),
                              test::Quad::SurfaceQuad(root_surface_id_, 1.f),
                              test::Quad::SolidColorQuad(SK_ColorMAGENTA)};
  test::Pass child_passes[] = {test::Pass(child_quads, arraysize(child_quads))};

  SubmitCompositorFrame(child_passes, arraysize(child_passes),
                        child_surface_id);

  // The child surface's reference to the root_surface_ will be dropped, so
  // we'll end up with:
  //   SK_ColorBLUE from the parent
  //   SK_ColorGREEN from the child
  //   SK_ColorMAGENTA from the child
  //   SK_ColorCYAN from the parent
  test::Quad expected_quads[] = {test::Quad::SolidColorQuad(SK_ColorBLUE),
                                 test::Quad::SolidColorQuad(SK_ColorGREEN),
                                 test::Quad::SolidColorQuad(SK_ColorMAGENTA),
                                 test::Quad::SolidColorQuad(SK_ColorCYAN)};
  test::Pass expected_passes[] = {
      test::Pass(expected_quads, arraysize(expected_quads))};
  SurfaceId ids[] = {root_surface_id_, child_surface_id};
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));
  AggregateAndVerify(
      expected_passes, arraysize(expected_passes), ids, arraysize(ids));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
  factory_.Destroy(child_surface_id);
}

// Tests that we map render pass IDs from different surfaces into a unified
// namespace and update RenderPassDrawQuad's id references to match.
TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassIdMapping) {
  SurfaceId child_surface_id = allocator_.GenerateId();
  factory_.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  RenderPassId child_pass_id[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
  test::Quad child_quad[][1] = {{test::Quad::SolidColorQuad(SK_ColorGREEN)},
                                {test::Quad::RenderPassQuad(child_pass_id[0])}};
  test::Pass surface_passes[] = {
      test::Pass(child_quad[0], arraysize(child_quad[0]), child_pass_id[0]),
      test::Pass(child_quad[1], arraysize(child_quad[1]), child_pass_id[1])};

  SubmitCompositorFrame(surface_passes, arraysize(surface_passes),
                        child_surface_id);

  // Pass IDs from the parent surface may collide with ones from the child.
  RenderPassId parent_pass_id[] = {RenderPassId(2, 1), RenderPassId(1, 2)};
  test::Quad parent_quad[][1] = {
      {test::Quad::SurfaceQuad(child_surface_id, 1.f)},
      {test::Quad::RenderPassQuad(parent_pass_id[0])}};
  test::Pass parent_passes[] = {
      test::Pass(parent_quad[0], arraysize(parent_quad[0]), parent_pass_id[0]),
      test::Pass(parent_quad[1], arraysize(parent_quad[1]), parent_pass_id[1])};

  SubmitCompositorFrame(parent_passes, arraysize(parent_passes),
                        root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(3u, aggregated_pass_list.size());
  RenderPassId actual_pass_ids[] = {aggregated_pass_list[0]->id,
                                    aggregated_pass_list[1]->id,
                                    aggregated_pass_list[2]->id};
  // Make sure the aggregated frame's pass IDs are all unique.
  for (size_t i = 0; i < 3; ++i) {
    for (size_t j = 0; j < i; ++j) {
      EXPECT_NE(actual_pass_ids[j], actual_pass_ids[i]) << "pass ids " << i
                                                        << " and " << j;
    }
  }

  // Make sure the render pass quads reference the remapped pass IDs.
  DrawQuad* render_pass_quads[] = {aggregated_pass_list[1]->quad_list.front(),
                                   aggregated_pass_list[2]->quad_list.front()};
  ASSERT_EQ(render_pass_quads[0]->material, DrawQuad::RENDER_PASS);
  EXPECT_EQ(
      actual_pass_ids[0],
      RenderPassDrawQuad::MaterialCast(render_pass_quads[0])->render_pass_id);

  ASSERT_EQ(render_pass_quads[1]->material, DrawQuad::RENDER_PASS);
  EXPECT_EQ(
      actual_pass_ids[1],
      RenderPassDrawQuad::MaterialCast(render_pass_quads[1])->render_pass_id);
  factory_.Destroy(child_surface_id);
}

void AddSolidColorQuadWithBlendMode(const gfx::Size& size,
                                    RenderPass* pass,
                                    const SkXfermode::Mode blend_mode) {
  const gfx::Transform layer_to_target_transform;
  const gfx::Size layer_bounds(size);
  const gfx::Rect visible_layer_rect(size);
  const gfx::Rect clip_rect(size);

  bool is_clipped = false;
  float opacity = 1.f;

  bool force_anti_aliasing_off = false;
  SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
  sqs->SetAll(layer_to_target_transform, layer_bounds, visible_layer_rect,
              clip_rect, is_clipped, opacity, blend_mode, 0);

  SolidColorDrawQuad* color_quad =
      pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
  color_quad->SetNew(pass->shared_quad_state_list.back(), visible_layer_rect,
                     visible_layer_rect, SK_ColorGREEN,
                     force_anti_aliasing_off);
}

// This tests that we update shared quad state pointers correctly within
// aggregated passes.  The shared quad state list on the aggregated pass will
// include the shared quad states from each pass in one list so the quads will
// end up pointed to shared quad state objects at different offsets. This test
// uses the blend_mode value stored on the shared quad state to track the shared
// quad state, but anything saved on the shared quad state would work.
//
// This test has 4 surfaces in the following structure:
// root_surface -> quad with kClear_Mode,
//                 [child_one_surface],
//                 quad with kDstOver_Mode,
//                 [child_two_surface],
//                 quad with kDstIn_Mode
// child_one_surface -> quad with kSrc_Mode,
//                      [grandchild_surface],
//                      quad with kSrcOver_Mode
// child_two_surface -> quad with kSrcIn_Mode
// grandchild_surface -> quad with kDst_Mode
//
// Resulting in the following aggregated pass:
//  quad_root_0       - blend_mode kClear_Mode
//  quad_child_one_0  - blend_mode kSrc_Mode
//  quad_grandchild_0 - blend_mode kDst_Mode
//  quad_child_one_1  - blend_mode kSrcOver_Mode
//  quad_root_1       - blend_mode kDstOver_Mode
//  quad_child_two_0  - blend_mode kSrcIn_Mode
//  quad_root_2       - blend_mode kDstIn_Mode
TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) {
  const SkXfermode::Mode blend_modes[] = {SkXfermode::kClear_Mode,    // 0
                                          SkXfermode::kSrc_Mode,      // 1
                                          SkXfermode::kDst_Mode,      // 2
                                          SkXfermode::kSrcOver_Mode,  // 3
                                          SkXfermode::kDstOver_Mode,  // 4
                                          SkXfermode::kSrcIn_Mode,    // 5
                                          SkXfermode::kDstIn_Mode,    // 6
  };

  RenderPassId pass_id(1, 1);
  SurfaceId grandchild_surface_id = allocator_.GenerateId();
  factory_.Create(grandchild_surface_id);
  Surface* grandchild_surface = manager_.GetSurfaceForId(grandchild_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(grandchild_surface));
  scoped_ptr<RenderPass> grandchild_pass = RenderPass::Create();
  gfx::Rect output_rect(SurfaceSize());
  gfx::Rect damage_rect(SurfaceSize());
  gfx::Transform transform_to_root_target;
  grandchild_pass->SetNew(
      pass_id, output_rect, damage_rect, transform_to_root_target);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), grandchild_pass.get(), blend_modes[2]);
  QueuePassAsFrame(grandchild_pass.Pass(), grandchild_surface_id);

  SurfaceId child_one_surface_id = allocator_.GenerateId();
  factory_.Create(child_one_surface_id);
  Surface* child_one_surface = manager_.GetSurfaceForId(child_one_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_one_surface));

  scoped_ptr<RenderPass> child_one_pass = RenderPass::Create();
  child_one_pass->SetNew(
      pass_id, output_rect, damage_rect, transform_to_root_target);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), child_one_pass.get(), blend_modes[1]);
  SurfaceDrawQuad* grandchild_surface_quad =
      child_one_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
  grandchild_surface_quad->SetNew(child_one_pass->shared_quad_state_list.back(),
                                  gfx::Rect(SurfaceSize()),
                                  gfx::Rect(SurfaceSize()),
                                  grandchild_surface_id);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), child_one_pass.get(), blend_modes[3]);
  QueuePassAsFrame(child_one_pass.Pass(), child_one_surface_id);

  SurfaceId child_two_surface_id = allocator_.GenerateId();
  factory_.Create(child_two_surface_id);
  Surface* child_two_surface = manager_.GetSurfaceForId(child_two_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_two_surface));

  scoped_ptr<RenderPass> child_two_pass = RenderPass::Create();
  child_two_pass->SetNew(
      pass_id, output_rect, damage_rect, transform_to_root_target);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), child_two_pass.get(), blend_modes[5]);
  QueuePassAsFrame(child_two_pass.Pass(), child_two_surface_id);

  scoped_ptr<RenderPass> root_pass = RenderPass::Create();
  root_pass->SetNew(
      pass_id, output_rect, damage_rect, transform_to_root_target);

  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), root_pass.get(), blend_modes[0]);
  SurfaceDrawQuad* child_one_surface_quad =
      root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
  child_one_surface_quad->SetNew(root_pass->shared_quad_state_list.back(),
                                 gfx::Rect(SurfaceSize()),
                                 gfx::Rect(SurfaceSize()),
                                 child_one_surface_id);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), root_pass.get(), blend_modes[4]);
  SurfaceDrawQuad* child_two_surface_quad =
      root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
  child_two_surface_quad->SetNew(root_pass->shared_quad_state_list.back(),
                                 gfx::Rect(SurfaceSize()),
                                 gfx::Rect(SurfaceSize()),
                                 child_two_surface_id);
  AddSolidColorQuadWithBlendMode(
      SurfaceSize(), root_pass.get(), blend_modes[6]);

  QueuePassAsFrame(root_pass.Pass(), root_surface_id_);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(grandchild_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_one_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_two_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(grandchild_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_one_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_two_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(1u, aggregated_pass_list.size());

  const QuadList& aggregated_quad_list = aggregated_pass_list[0]->quad_list;

  ASSERT_EQ(7u, aggregated_quad_list.size());

  for (auto iter = aggregated_quad_list.cbegin();
       iter != aggregated_quad_list.cend();
       ++iter) {
    EXPECT_EQ(blend_modes[iter.index()], iter->shared_quad_state->blend_mode)
        << iter.index();
  }
  factory_.Destroy(child_one_surface_id);
  factory_.Destroy(child_two_surface_id);
  factory_.Destroy(grandchild_surface_id);
}

// This tests that when aggregating a frame with multiple render passes that we
// map the transforms for the root pass but do not modify the transform on child
// passes.
//
// The root surface has one pass with a surface quad transformed by +10 in the y
// direction.
//
// The middle surface has one pass with a surface quad scaled by 2 in the x
// and 3 in the y directions.
//
// The child surface has two passes. The first pass has a quad with a transform
// of +5 in the x direction. The second pass has a reference to the first pass'
// pass id and a transform of +8 in the x direction.
//
// After aggregation, the child surface's root pass quad should have all
// transforms concatenated for a total transform of +23 x, +10 y. The
// contributing render pass' transform in the aggregate frame should not be
// affected.
TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
  // Innermost child surface.
  SurfaceId child_surface_id = allocator_.GenerateId();
  factory_.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));
  {
    RenderPassId child_pass_id[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
    test::Quad child_quads[][1] = {
        {test::Quad::SolidColorQuad(SK_ColorGREEN)},
        {test::Quad::RenderPassQuad(child_pass_id[0])},
    };
    test::Pass child_passes[] = {
        test::Pass(child_quads[0], arraysize(child_quads[0]), child_pass_id[0]),
        test::Pass(child_quads[1], arraysize(child_quads[1]),
                   child_pass_id[1])};

    RenderPassList child_pass_list;
    AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
              arraysize(child_passes));

    RenderPass* child_nonroot_pass = child_pass_list.at(0u);
    child_nonroot_pass->transform_to_root_target.Translate(8, 0);
    SharedQuadState* child_nonroot_pass_sqs =
        child_nonroot_pass->shared_quad_state_list.front();
    child_nonroot_pass_sqs->quad_to_target_transform.Translate(5, 0);

    RenderPass* child_root_pass = child_pass_list.at(1u);
    SharedQuadState* child_root_pass_sqs =
        child_root_pass->shared_quad_state_list.front();
    child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
    child_root_pass_sqs->is_clipped = true;
    child_root_pass_sqs->clip_rect = gfx::Rect(0, 0, 5, 5);

    scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData);
    child_pass_list.swap(child_frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> child_frame(new CompositorFrame);
    child_frame->delegated_frame_data = child_frame_data.Pass();

    factory_.SubmitCompositorFrame(child_surface_id, child_frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

  // Middle child surface.
  SurfaceId middle_surface_id = allocator_.GenerateId();
  factory_.Create(middle_surface_id);
  Surface* middle_surface = manager_.GetSurfaceForId(middle_surface_id);
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(middle_surface));
  {
    test::Quad middle_quads[] = {
        test::Quad::SurfaceQuad(child_surface_id, 1.f)};
    test::Pass middle_passes[] = {
        test::Pass(middle_quads, arraysize(middle_quads)),
    };

    RenderPassList middle_pass_list;
    AddPasses(&middle_pass_list, gfx::Rect(SurfaceSize()), middle_passes,
              arraysize(middle_passes));

    RenderPass* middle_root_pass = middle_pass_list.at(0u);
    middle_root_pass->quad_list.ElementAt(0)->visible_rect =
        gfx::Rect(0, 1, 100, 7);
    SharedQuadState* middle_root_pass_sqs =
        middle_root_pass->shared_quad_state_list.front();
    middle_root_pass_sqs->quad_to_target_transform.Scale(2, 3);

    scoped_ptr<DelegatedFrameData> middle_frame_data(new DelegatedFrameData);
    middle_pass_list.swap(middle_frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> middle_frame(new CompositorFrame);
    middle_frame->delegated_frame_data = middle_frame_data.Pass();

    factory_.SubmitCompositorFrame(middle_surface_id, middle_frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

  // Root surface.
  test::Quad secondary_quads[] = {
      test::Quad::SolidColorQuad(1),
      test::Quad::SurfaceQuad(middle_surface_id, 1.f)};
  test::Quad root_quads[] = {test::Quad::SolidColorQuad(1)};
  test::Pass root_passes[] = {
      test::Pass(secondary_quads, arraysize(secondary_quads)),
      test::Pass(root_quads, arraysize(root_quads))};

  RenderPassList root_pass_list;
  AddPasses(&root_pass_list,
            gfx::Rect(SurfaceSize()),
            root_passes,
            arraysize(root_passes));

  root_pass_list.at(0)
      ->shared_quad_state_list.front()
      ->quad_to_target_transform.Translate(0, 7);
  root_pass_list.at(0)
      ->shared_quad_state_list.ElementAt(1)
      ->quad_to_target_transform.Translate(0, 10);
  root_pass_list.at(0)->quad_list.ElementAt(1)->visible_rect =
      gfx::Rect(0, 0, 8, 100);

  root_pass_list[0]->transform_to_root_target.Translate(10, 5);

  scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
  root_pass_list.swap(root_frame_data->render_pass_list);

  scoped_ptr<CompositorFrame> root_frame(new CompositorFrame);
  root_frame->delegated_frame_data = root_frame_data.Pass();

  factory_.SubmitCompositorFrame(root_surface_id_, root_frame.Pass(),
                                 SurfaceFactory::DrawCallback());

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(middle_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(middle_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(3u, aggregated_pass_list.size());

  ASSERT_EQ(1u, aggregated_pass_list[0]->shared_quad_state_list.size());

  // The first pass should have one shared quad state for the one solid color
  // quad.
  EXPECT_EQ(1u, aggregated_pass_list[0]->shared_quad_state_list.size());
  // The second pass should have just two shared quad states. We'll
  // verify the properties through the quads.
  EXPECT_EQ(2u, aggregated_pass_list[1]->shared_quad_state_list.size());

  EXPECT_EQ(1u, aggregated_pass_list[2]->shared_quad_state_list.size());

  SharedQuadState* aggregated_first_pass_sqs =
      aggregated_pass_list[0]->shared_quad_state_list.front();

  // The first pass's transform should be unaffected by the embedding and still
  // be a translation by +5 in the x direction.
  gfx::Transform expected_aggregated_first_pass_sqs_transform;
  expected_aggregated_first_pass_sqs_transform.Translate(5, 0);
  EXPECT_EQ(expected_aggregated_first_pass_sqs_transform.ToString(),
            aggregated_first_pass_sqs->quad_to_target_transform.ToString());

  // The first pass's transform to the root target should include the aggregated
  // transform, including the transform from the child pass to the root.
  gfx::Transform expected_first_pass_transform_to_root_target;
  expected_first_pass_transform_to_root_target.Translate(10, 5);
  expected_first_pass_transform_to_root_target.Translate(0, 10);
  expected_first_pass_transform_to_root_target.Scale(2, 3);
  expected_first_pass_transform_to_root_target.Translate(8, 0);
  EXPECT_EQ(expected_first_pass_transform_to_root_target.ToString(),
            aggregated_pass_list[0]->transform_to_root_target.ToString());

  ASSERT_EQ(2u, aggregated_pass_list[1]->quad_list.size());

  gfx::Transform expected_root_pass_quad_transforms[2];
  // The first quad in the root pass is the solid color quad from the original
  // root surface. Its transform should be unaffected by the aggregation and
  // still be +7 in the y direction.
  expected_root_pass_quad_transforms[0].Translate(0, 7);
  // The second quad in the root pass is aggregated from the child surface so
  // its transform should be the combination of its original translation
  // (0, 10), the middle surface draw quad's scale of (2, 3), and the
  // child surface draw quad's translation (8, 0).
  expected_root_pass_quad_transforms[1].Translate(0, 10);
  expected_root_pass_quad_transforms[1].Scale(2, 3);
  expected_root_pass_quad_transforms[1].Translate(8, 0);

  for (auto iter = aggregated_pass_list[1]->quad_list.cbegin();
       iter != aggregated_pass_list[1]->quad_list.cend();
       ++iter) {
    EXPECT_EQ(expected_root_pass_quad_transforms[iter.index()].ToString(),
              iter->shared_quad_state->quad_to_target_transform.ToString())
        << iter.index();
  }

  EXPECT_TRUE(
      aggregated_pass_list[1]->shared_quad_state_list.ElementAt(1)->is_clipped);

  // The second quad in the root pass is aggregated from the child, so its
  // clip rect must be transformed by the child's translation/scale and
  // clipped be the visible_rects for both children.
  EXPECT_EQ(gfx::Rect(0, 13, 8, 12).ToString(),
            aggregated_pass_list[1]
                ->shared_quad_state_list.ElementAt(1)
                ->clip_rect.ToString());

  factory_.Destroy(middle_surface_id);
  factory_.Destroy(child_surface_id);
}

// Tests that damage rects are aggregated correctly when surfaces change.
TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
  test::Quad child_quads[] = {test::Quad::RenderPassQuad(RenderPassId(1, 1))};
  test::Pass child_passes[] = {
      test::Pass(child_quads, arraysize(child_quads), RenderPassId(1, 1))};

  RenderPassList child_pass_list;
  AddPasses(&child_pass_list,
            gfx::Rect(SurfaceSize()),
            child_passes,
            arraysize(child_passes));

  RenderPass* child_root_pass = child_pass_list.at(0u);
  SharedQuadState* child_root_pass_sqs =
      child_root_pass->shared_quad_state_list.front();
  child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);

  scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData);
  child_pass_list.swap(child_frame_data->render_pass_list);

  scoped_ptr<CompositorFrame> child_frame(new CompositorFrame);
  child_frame->delegated_frame_data = child_frame_data.Pass();

  SurfaceId child_surface_id = allocator_.GenerateId();
  factory_.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);
  factory_.SubmitCompositorFrame(child_surface_id, child_frame.Pass(),
                                 SurfaceFactory::DrawCallback());

  test::Quad parent_surface_quads[] = {
      test::Quad::SurfaceQuad(child_surface_id, 1.f)};
  test::Pass parent_surface_passes[] = {
      test::Pass(parent_surface_quads, arraysize(parent_surface_quads),
                 RenderPassId(1, 1))};

  RenderPassList parent_surface_pass_list;
  AddPasses(&parent_surface_pass_list,
            gfx::Rect(SurfaceSize()),
            parent_surface_passes,
            arraysize(parent_surface_passes));

  // Parent surface is only used to test if the transform is applied correctly
  // to the child surface's damage.
  scoped_ptr<DelegatedFrameData> parent_surface_frame_data(
      new DelegatedFrameData);
  parent_surface_pass_list.swap(parent_surface_frame_data->render_pass_list);

  scoped_ptr<CompositorFrame> parent_surface_frame(new CompositorFrame);
  parent_surface_frame->delegated_frame_data = parent_surface_frame_data.Pass();

  SurfaceId parent_surface_id = allocator_.GenerateId();
  factory_.Create(parent_surface_id);
  Surface* parent_surface = manager_.GetSurfaceForId(parent_surface_id);
  factory_.SubmitCompositorFrame(parent_surface_id, parent_surface_frame.Pass(),
                                 SurfaceFactory::DrawCallback());

  test::Quad root_surface_quads[] = {
      test::Quad::SurfaceQuad(parent_surface_id, 1.f)};
  test::Quad root_render_pass_quads[] = {
      test::Quad::RenderPassQuad(RenderPassId(1, 1))};

  test::Pass root_passes[] = {
      test::Pass(root_surface_quads, arraysize(root_surface_quads),
                 RenderPassId(1, 1)),
      test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads),
                 RenderPassId(2, 1))};

  RenderPassList root_pass_list;
  AddPasses(&root_pass_list,
            gfx::Rect(SurfaceSize()),
            root_passes,
            arraysize(root_passes));

  root_pass_list.at(0)
      ->shared_quad_state_list.front()
      ->quad_to_target_transform.Translate(0, 10);
  root_pass_list.at(0)->damage_rect = gfx::Rect(5, 5, 10, 10);
  root_pass_list.at(1)->damage_rect = gfx::Rect(5, 5, 100, 100);

  scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
  root_pass_list.swap(root_frame_data->render_pass_list);

  scoped_ptr<CompositorFrame> root_frame(new CompositorFrame);
  root_frame->delegated_frame_data = root_frame_data.Pass();

  factory_.SubmitCompositorFrame(root_surface_id_, root_frame.Pass(),
                                 SurfaceFactory::DrawCallback());

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(parent_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(2u, aggregated_pass_list.size());

  // Damage rect for first aggregation should contain entire root surface.
  EXPECT_TRUE(
      aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));

  {
    AddPasses(&child_pass_list,
              gfx::Rect(SurfaceSize()),
              child_passes,
              arraysize(child_passes));

    RenderPass* child_root_pass = child_pass_list.at(0u);
    SharedQuadState* child_root_pass_sqs =
        child_root_pass->shared_quad_state_list.front();
    child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
    child_root_pass->damage_rect = gfx::Rect(10, 10, 10, 10);

    scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData);
    child_pass_list.swap(child_frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> child_frame(new CompositorFrame);
    child_frame->delegated_frame_data = child_frame_data.Pass();

    factory_.SubmitCompositorFrame(child_surface_id, child_frame.Pass(),
                                   SurfaceFactory::DrawCallback());

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    ASSERT_EQ(2u, aggregated_pass_list.size());

    // Outer surface didn't change, so transformed inner damage rect should be
    // used.
    EXPECT_EQ(gfx::Rect(10, 20, 10, 10).ToString(),
              aggregated_pass_list[1]->damage_rect.ToString());
  }

  {
    RenderPassList root_pass_list;
    AddPasses(&root_pass_list,
              gfx::Rect(SurfaceSize()),
              root_passes,
              arraysize(root_passes));

    root_pass_list.at(0)
        ->shared_quad_state_list.front()
        ->quad_to_target_transform.Translate(0, 10);
    root_pass_list.at(0)->damage_rect = gfx::Rect(0, 0, 1, 1);

    scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
    root_pass_list.swap(root_frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> root_frame(new CompositorFrame);
    root_frame->delegated_frame_data = root_frame_data.Pass();

    factory_.SubmitCompositorFrame(root_surface_id_, root_frame.Pass(),
                                   SurfaceFactory::DrawCallback());
  }

  {
    RenderPassList root_pass_list;
    AddPasses(&root_pass_list,
              gfx::Rect(SurfaceSize()),
              root_passes,
              arraysize(root_passes));

    root_pass_list.at(0)
        ->shared_quad_state_list.front()
        ->quad_to_target_transform.Translate(0, 10);
    root_pass_list.at(0)->damage_rect = gfx::Rect(1, 1, 1, 1);

    scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
    root_pass_list.swap(root_frame_data->render_pass_list);

    scoped_ptr<CompositorFrame> root_frame(new CompositorFrame);
    root_frame->delegated_frame_data = root_frame_data.Pass();

    factory_.SubmitCompositorFrame(root_surface_id_, root_frame.Pass(),
                                   SurfaceFactory::DrawCallback());

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    ASSERT_EQ(2u, aggregated_pass_list.size());

    // The root surface was enqueued without being aggregated once, so it should
    // be treated as completely damaged.
    EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
        gfx::Rect(SurfaceSize())));
  }

  // No Surface changed, so no damage should be given.
  {
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    ASSERT_EQ(2u, aggregated_pass_list.size());

    EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.IsEmpty());
  }

  // SetFullDamageRectForSurface should cause the entire output to be
  // marked as damaged.
  {
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    aggregator_.SetFullDamageForSurface(root_surface_id_);
    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(parent_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    ASSERT_EQ(2u, aggregated_pass_list.size());

    EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
        gfx::Rect(SurfaceSize())));
  }

  factory_.Destroy(child_surface_id);
}

class SurfaceAggregatorPartialSwapTest
    : public SurfaceAggregatorValidSurfaceTest {
 public:
  SurfaceAggregatorPartialSwapTest()
      : SurfaceAggregatorValidSurfaceTest(true) {}
};

// Tests that quads outside the damage rect are ignored.
TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
  SurfaceId child_surface_id = allocator_.GenerateId();
  factory_.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);
  // The child surface has two quads, one with a visible rect of 13,13 4x4 and
  // the other other with a visible rect of 10,10 2x2 (relative to root target
  // space).
  {
    RenderPassId child_pass_id = RenderPassId(1, 1);
    test::Quad child_quads1[] = {test::Quad::RenderPassQuad(child_pass_id)};
    test::Quad child_quads2[] = {test::Quad::RenderPassQuad(child_pass_id)};
    test::Pass child_passes[] = {
        test::Pass(child_quads1, arraysize(child_quads1), child_pass_id),
        test::Pass(child_quads2, arraysize(child_quads2), child_pass_id)};

    RenderPassList child_pass_list;
    AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
              arraysize(child_passes));

    child_pass_list.at(0u)->quad_list.ElementAt(0)->visible_rect =
        gfx::Rect(1, 1, 2, 2);
    SharedQuadState* child_sqs =
        child_pass_list.at(0u)->shared_quad_state_list.ElementAt(0u);
    child_sqs->quad_to_target_transform.Translate(1, 1);
    child_sqs->quad_to_target_transform.Scale(2, 2);

    child_pass_list.at(1u)->quad_list.ElementAt(0)->visible_rect =
        gfx::Rect(0, 0, 2, 2);

    SubmitPassListAsFrame(child_surface_id, &child_pass_list);
  }

  {
    test::Quad root_quads[] = {test::Quad::SurfaceQuad(child_surface_id, 1.f)};

    test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))};

    RenderPassList root_pass_list;
    AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
              arraysize(root_passes));

    RenderPass* root_pass = root_pass_list.at(0u);
    root_pass->shared_quad_state_list.front()
        ->quad_to_target_transform.Translate(10, 10);
    root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);

    SubmitPassListAsFrame(root_surface_id_, &root_pass_list);
  }

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  scoped_ptr<CompositorFrame> aggregated_frame =
      aggregator_.Aggregate(root_surface_id_);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

  ASSERT_TRUE(aggregated_frame);
  ASSERT_TRUE(aggregated_frame->delegated_frame_data);

  DelegatedFrameData* frame_data = aggregated_frame->delegated_frame_data.get();

  const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

  ASSERT_EQ(2u, aggregated_pass_list.size());

  // Damage rect for first aggregation should contain entire root surface.
  EXPECT_EQ(gfx::Rect(0, 0, 15, 15), aggregated_pass_list[1]->damage_rect);
  EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
  EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());

  // Create a root surface with a smaller damage rect.
  {
    test::Quad root_quads[] = {test::Quad::SurfaceQuad(child_surface_id, 1.f)};

    test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))};

    RenderPassList root_pass_list;
    AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
              arraysize(root_passes));

    RenderPass* root_pass = root_pass_list.at(0u);
    root_pass->shared_quad_state_list.front()
        ->quad_to_target_transform.Translate(10, 10);
    root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
    SubmitPassListAsFrame(root_surface_id_, &root_pass_list);
  }

  {
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    ASSERT_EQ(2u, aggregated_pass_list.size());

    // Only first quad from surface is inside damage rect and should be
    // included.
    EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[1]->damage_rect);
    EXPECT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
    EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
    EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
              aggregated_pass_list[1]->quad_list.back()->visible_rect);
  }

  // New child frame has same content and no damage, but has a
  // CopyOutputRequest.
  {
    RenderPassId child_pass_id = RenderPassId(1, 1);
    test::Quad child_quads1[] = {test::Quad::RenderPassQuad(child_pass_id)};
    test::Quad child_quads2[] = {test::Quad::RenderPassQuad(child_pass_id)};
    test::Pass child_passes[] = {
        test::Pass(child_quads1, arraysize(child_quads1), child_pass_id),
        test::Pass(child_quads2, arraysize(child_quads2), child_pass_id)};

    RenderPassList child_pass_list;
    AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
              arraysize(child_passes));

    child_pass_list.at(0u)->quad_list.ElementAt(0)->visible_rect =
        gfx::Rect(1, 1, 2, 2);
    SharedQuadState* child_sqs =
        child_pass_list.at(0u)->shared_quad_state_list.ElementAt(0u);
    child_sqs->quad_to_target_transform.Translate(1, 1);
    child_sqs->quad_to_target_transform.Scale(2, 2);

    child_pass_list.at(1u)->quad_list.ElementAt(0)->visible_rect =
        gfx::Rect(0, 0, 2, 2);

    RenderPass* child_root_pass = child_pass_list.at(1u);

    child_root_pass->copy_requests.push_back(
        CopyOutputRequest::CreateEmptyRequest());
    child_root_pass->damage_rect = gfx::Rect();
    SubmitPassListAsFrame(child_surface_id, &child_pass_list);
  }

  {
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;

    // Output frame should have no damage, but all quads included.
    ASSERT_EQ(3u, aggregated_pass_list.size());

    EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.IsEmpty());
    ASSERT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
    ASSERT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
    EXPECT_EQ(gfx::Rect(1, 1, 2, 2),
              aggregated_pass_list[0]->quad_list.ElementAt(0)->visible_rect);
    EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
              aggregated_pass_list[1]->quad_list.ElementAt(0)->visible_rect);
  }

  {
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    scoped_ptr<CompositorFrame> aggregated_frame =
        aggregator_.Aggregate(root_surface_id_);

    EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface_));
    EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

    ASSERT_TRUE(aggregated_frame);
    ASSERT_TRUE(aggregated_frame->delegated_frame_data);

    DelegatedFrameData* frame_data =
        aggregated_frame->delegated_frame_data.get();

    const RenderPassList& aggregated_pass_list = frame_data->render_pass_list;
    // There were no changes since last aggregation, so output should be empty
    // and have no damage.
    ASSERT_EQ(1u, aggregated_pass_list.size());
    EXPECT_TRUE(aggregated_pass_list[0]->damage_rect.IsEmpty());
    ASSERT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
  }

  factory_.Destroy(child_surface_id);
}

class SurfaceAggregatorWithResourcesTest : public testing::Test {
 public:
  void SetUp() override {
    output_surface_ = FakeOutputSurface::CreateSoftware(
        make_scoped_ptr(new SoftwareOutputDevice));
    output_surface_->BindToClient(&output_surface_client_);
    shared_bitmap_manager_.reset(new TestSharedBitmapManager);

    resource_provider_ = FakeResourceProvider::Create(
        output_surface_.get(), shared_bitmap_manager_.get());

    aggregator_.reset(new SurfaceAggregator(&surface_aggregator_client_,
                                            &manager_, resource_provider_.get(),
                                            false));
  }

 protected:
  SurfaceManager manager_;
  FakeOutputSurfaceClient output_surface_client_;
  scoped_ptr<OutputSurface> output_surface_;
  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
  scoped_ptr<ResourceProvider> resource_provider_;
  scoped_ptr<SurfaceAggregator> aggregator_;
  FakeSurfaceAggregatorClient surface_aggregator_client_;
};

class ResourceTrackingSurfaceFactoryClient : public SurfaceFactoryClient {
 public:
  ResourceTrackingSurfaceFactoryClient() {}
  ~ResourceTrackingSurfaceFactoryClient() override {}

  void ReturnResources(const ReturnedResourceArray& resources) override {
    returned_resources_ = resources;
  }

  ReturnedResourceArray returned_resources() const {
    return returned_resources_;
  }

  void SetBeginFrameSource(SurfaceId surface_id,
                           BeginFrameSource* begin_frame_source) override {}

 private:
  ReturnedResourceArray returned_resources_;

  DISALLOW_COPY_AND_ASSIGN(ResourceTrackingSurfaceFactoryClient);
};

void SubmitCompositorFrameWithResources(ResourceId* resource_ids,
                                        size_t num_resource_ids,
                                        bool valid,
                                        SurfaceId child_id,
                                        SurfaceFactory* factory,
                                        SurfaceId surface_id) {
  scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
  scoped_ptr<RenderPass> pass = RenderPass::Create();
  pass->id = RenderPassId(1, 1);
  SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
  sqs->opacity = 1.f;
  if (!child_id.is_null()) {
    SurfaceDrawQuad* surface_quad =
        pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
    surface_quad->SetNew(sqs, gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1),
                         child_id);
  }

  for (size_t i = 0u; i < num_resource_ids; ++i) {
    TransferableResource resource;
    resource.id = resource_ids[i];
    // ResourceProvider is software, so only software resources are valid.
    resource.is_software = valid;
    frame_data->resource_list.push_back(resource);
    TextureDrawQuad* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
    const gfx::Rect rect;
    const gfx::Rect opaque_rect;
    const gfx::Rect visible_rect;
    bool needs_blending = false;
    bool premultiplied_alpha = false;
    const gfx::PointF uv_top_left;
    const gfx::PointF uv_bottom_right;
    SkColor background_color = SK_ColorGREEN;
    const float vertex_opacity[4] = {0.f, 0.f, 1.f, 1.f};
    bool flipped = false;
    bool nearest_neighbor = false;
    quad->SetAll(sqs, rect, opaque_rect, visible_rect, needs_blending,
                 resource_ids[i], gfx::Size(), premultiplied_alpha, uv_top_left,
                 uv_bottom_right, background_color, vertex_opacity, flipped,
                 nearest_neighbor);
  }
  frame_data->render_pass_list.push_back(pass.Pass());
  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
  frame->delegated_frame_data = frame_data.Pass();
  factory->SubmitCompositorFrame(surface_id, frame.Pass(),
                                 SurfaceFactory::DrawCallback());
}

TEST_F(SurfaceAggregatorWithResourcesTest, TakeResourcesOneSurface) {
  ResourceTrackingSurfaceFactoryClient client;
  SurfaceFactory factory(&manager_, &client);
  SurfaceId surface_id(7u);
  factory.Create(surface_id);
  Surface* surface = manager_.GetSurfaceForId(surface_id);

  ResourceId ids[] = {11, 12, 13};
  SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                     &factory, surface_id);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface));

  scoped_ptr<CompositorFrame> frame = aggregator_->Aggregate(surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface));

  // Nothing should be available to be returned yet.
  EXPECT_TRUE(client.returned_resources().empty());

  SubmitCompositorFrameWithResources(NULL, 0u, true, SurfaceId(), &factory,
                                     surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface));

  frame = aggregator_->Aggregate(surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface));

  ASSERT_EQ(3u, client.returned_resources().size());
  ResourceId returned_ids[3];
  for (size_t i = 0; i < 3; ++i) {
    returned_ids[i] = client.returned_resources()[i].id;
  }
  EXPECT_THAT(returned_ids,
              testing::WhenSorted(testing::ElementsAreArray(ids)));
  factory.Destroy(surface_id);
}

TEST_F(SurfaceAggregatorWithResourcesTest, TakeInvalidResources) {
  ResourceTrackingSurfaceFactoryClient client;
  SurfaceFactory factory(&manager_, &client);
  SurfaceId surface_id(7u);
  factory.Create(surface_id);
  Surface* surface = manager_.GetSurfaceForId(surface_id);

  scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
  scoped_ptr<RenderPass> pass = RenderPass::Create();
  pass->id = RenderPassId(1, 1);
  TransferableResource resource;
  resource.id = 11;
  // ResourceProvider is software but resource is not, so it should be
  // ignored.
  resource.is_software = false;
  frame_data->resource_list.push_back(resource);
  frame_data->render_pass_list.push_back(pass.Pass());
  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
  frame->delegated_frame_data = frame_data.Pass();
  factory.SubmitCompositorFrame(surface_id, frame.Pass(),
                                SurfaceFactory::DrawCallback());

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface));

  scoped_ptr<CompositorFrame> returned_frame =
      aggregator_->Aggregate(surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface));

  // Nothing should be available to be returned yet.
  EXPECT_TRUE(client.returned_resources().empty());

  SubmitCompositorFrameWithResources(NULL, 0, true, SurfaceId(), &factory,
                                     surface_id);
  ASSERT_EQ(1u, client.returned_resources().size());
  EXPECT_EQ(11u, client.returned_resources()[0].id);

  factory.Destroy(surface_id);
}

TEST_F(SurfaceAggregatorWithResourcesTest, TwoSurfaces) {
  ResourceTrackingSurfaceFactoryClient client;
  SurfaceFactory factory(&manager_, &client);
  SurfaceId surface1_id(7u);
  factory.Create(surface1_id);
  Surface* surface1 = manager_.GetSurfaceForId(surface1_id);

  SurfaceId surface2_id(8u);
  factory.Create(surface2_id);
  Surface* surface2 = manager_.GetSurfaceForId(surface2_id);

  ResourceId ids[] = {11, 12, 13};
  SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                     &factory, surface1_id);
  ResourceId ids2[] = {14, 15, 16};
  SubmitCompositorFrameWithResources(ids2, arraysize(ids2), true, SurfaceId(),
                                     &factory, surface2_id);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface1));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface2));

  scoped_ptr<CompositorFrame> frame = aggregator_->Aggregate(surface1_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface1));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface2));

  SubmitCompositorFrameWithResources(NULL, 0, true, SurfaceId(), &factory,
                                     surface1_id);

  // Nothing should be available to be returned yet.
  EXPECT_TRUE(client.returned_resources().empty());

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface1));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface2));

  frame = aggregator_->Aggregate(surface2_id);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(surface1));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(surface2));

  // surface1_id wasn't referenced, so its resources should be returned.
  ASSERT_EQ(3u, client.returned_resources().size());
  ResourceId returned_ids[3];
  for (size_t i = 0; i < 3; ++i) {
    returned_ids[i] = client.returned_resources()[i].id;
  }
  EXPECT_THAT(returned_ids,
              testing::WhenSorted(testing::ElementsAreArray(ids)));
  EXPECT_EQ(3u, resource_provider_->num_resources());
  factory.Destroy(surface1_id);
  factory.Destroy(surface2_id);
}

// Ensure that aggregator completely ignores Surfaces that reference invalid
// resources.
TEST_F(SurfaceAggregatorWithResourcesTest, InvalidChildSurface) {
  ResourceTrackingSurfaceFactoryClient client;
  SurfaceFactory factory(&manager_, &client);
  SurfaceId root_surface_id(7u);
  factory.Create(root_surface_id);
  Surface* root_surface = manager_.GetSurfaceForId(root_surface_id);
  SurfaceId middle_surface_id(8u);
  factory.Create(middle_surface_id);
  Surface* middle_surface = manager_.GetSurfaceForId(middle_surface_id);
  SurfaceId child_surface_id(9u);
  factory.Create(child_surface_id);
  Surface* child_surface = manager_.GetSurfaceForId(child_surface_id);

  ResourceId ids[] = {14, 15, 16};
  SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                     &factory, child_surface_id);

  ResourceId ids2[] = {17, 18, 19};
  SubmitCompositorFrameWithResources(ids2, arraysize(ids2), false,
                                     child_surface_id, &factory,
                                     middle_surface_id);

  ResourceId ids3[] = {20, 21, 22};
  SubmitCompositorFrameWithResources(ids3, arraysize(ids3), true,
                                     middle_surface_id, &factory,
                                     root_surface_id);

  EXPECT_FALSE(surface_aggregator_client_.HasSurface(root_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(middle_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  scoped_ptr<CompositorFrame> frame;
  frame = aggregator_->Aggregate(root_surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(middle_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  RenderPassList* pass_list = &frame->delegated_frame_data->render_pass_list;
  ASSERT_EQ(1u, pass_list->size());
  EXPECT_EQ(1u, pass_list->back()->shared_quad_state_list.size());
  EXPECT_EQ(3u, pass_list->back()->quad_list.size());

  SubmitCompositorFrameWithResources(ids2, arraysize(ids), true,
                                     child_surface_id, &factory,
                                     middle_surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(middle_surface));
  EXPECT_FALSE(surface_aggregator_client_.HasSurface(child_surface));

  frame = aggregator_->Aggregate(root_surface_id);

  EXPECT_TRUE(surface_aggregator_client_.HasSurface(root_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(middle_surface));
  EXPECT_TRUE(surface_aggregator_client_.HasSurface(child_surface));

  pass_list = &frame->delegated_frame_data->render_pass_list;
  ASSERT_EQ(1u, pass_list->size());
  EXPECT_EQ(3u, pass_list->back()->shared_quad_state_list.size());
  EXPECT_EQ(9u, pass_list->back()->quad_list.size());

  factory.Destroy(root_surface_id);
  factory.Destroy(child_surface_id);
  factory.Destroy(middle_surface_id);
}

}  // namespace
}  // namespace cc


text:
Microsoft Office development platform ups and downs
Gates pledges more ribbon UI capabilities for Windows
Keep Windows XP until 2009, analysts tell Microsoft
Why people hate Vista
Microsoft's ribbon interface draws frowns, smiles
The Office Fluent UI changes the desktop paradigm -- and opinion is mixed as to whether that's a good thing
By Paul Krill
Editor at Large, InfoWorld |
Microsoft's Office 2007, and to a lesser extent Vista, is bringing a radical change to the desktop user interface. The Office Fluent UI feature -- the ribbon interface -- introduced in Office 2007 is Microsoft's attempt to help users get more out of Office applications. For nearly two decades, apps have relied on the menu interface to collect and expose features, but as programs' capabilities have ballooned, the menus have gotten overloaded. Software developers have tried various supplements, such as Office 2003's approach of hiding infrequently used menu items automatically, and Adobe's use of floating multi-tabbed feature panels -- approaches picked up by other developers as well.
But the ribbon interface is perhaps the most radical UI change since Windows 95 and the Mac's System 7 from the early 1990s. The ribbon presents commands organized as a set of commands most relevant for each task area. For example, Word has a tab labeled "References" for working with Word documents. The ribbon replaces menus and toolbars.
Its reception has been mixed.
[ See what InfoWorld SMB IT blogger has to say about living with Office 2007 ]
Why Microsoft made such a radical change
Microsoft officials, of course, laud the technology. In a presentation at the Microsoft's 2008 Office System Developer Conference in San Jose, Calif. last month, Microsoft Chairman Bill Gates characterized the Office Fluent UI as a success and said the company would be adding its capabilities to other systems. "We usability-tested it massively and, fortunately, it has had a very strong positive reaction," Gates said. The older interface obscured functionality, with Microsoft getting requests for capabilities that were already there, he said.
In developing the Office Fluent UI, Microsoft was looking for a way to handle the growing number of commands in Office, since the menus and toolbars couldn't handle them any longer, said Mark Alexieff, a senior product manager for Microsoft Office. "We got to a point where 10 years after [applications such as Word, Excel and PowerPoint] were released, the applications had 10 times more commands than they had at the beginning," he said. Users wanted to spend less time looking for features and figuring out how to use them, said Alexieff. "We shifted the focus to what are the ways we can most directly get people to the results they want," he said.
The use of an icon-laden ribbon also helped reduce screen real estate taken by the UI. "The design of the ribbon gave us the ability to use graphics [and] icons much more efficiently to help label commands," said Alexieff.
Why some people dislike Office Fluent
But there is a groundswell of opposition to the Office Fluent UI, despite Microsoft's testing.
"I dislike it. My wife dislikes it too," said Markian Zadony, who works at a financial services company. The Fluent UI is too radical of a departure, he said: "It's being forced down your throat, in essence."
His frustration centers around the fact that the ribbon tries to assume what a user wants to do, Zadony said -- often incorrectly. "It's really trying to be smarter than you," Zadony said.
"A very big distinction, in addition to taking up a pretty large chunk of screen real estate, the ribbon also makes some assumptions about what you want to be seeing," said one commenter in an InfoWorld discussion thread. The public comment section of an MSDN blog also attracted criticisms, with one commenter calling the ribbon "ridiculous" and another saying Microsoft created problems with its introduction. "Office 2007 is hideous. The UI is abominable," said another post in the blog.
Distaste for Fluent UI is broad enough that it has even created a new market opportunity. A Chinese company, Addintools, is offering users a chance to revert back to the old-style menus with its Classic Menu for Office 2007. Since its February 2007 release, the program has gained more than 35,000 users, the company said.
With Office Fluent, Microsoft has taken away familiarity from users, said analyst Judith Hurwitz, president of Hurwitz & Associates. In providing users a predetermined menu of capabilities, Microsoft is starting to introduce a form of artificial intelligence to the user interface, she said. "[However], it's very hard for a system to really understand what it is that a customer wants to do."
Hurwitz is not convinced the UI change was for customers' benefit: "I think the issue is change for the sake of change." Microsoft "broke some things that didn't need to be broken," she added, noting that her comments reflect what she has heard from Office customers.
[ Learn what Office 2007 developers think about the new version ]
Why some people like Office Fluent
Some adopters are enthusiastic: Gary Wilhelm, a business and financial systems manager at Englewood Hospital and Medical Center in New Jersey, said he finds that Office Fluent makes it easier to select a style or font in word. "You can kind of hover [over], highlight the information and select your font," he said. He suspects initial negative reaction to Office Fluent may be due to the significant change it imposes on users: "It took some getting used to because it was a big change."
Also among the supporters is Kevin Smyth, CIO at Cerner, a health care information technology provider. "I think my adoption curve was maybe about a week or so," he said. The software brings to the forefront capabilities that had been buried in menus, he said. Smyth also said he believes the interface has made Cerner employees more productivity.
The 2007 Office System client covers applications such as Excel and PowerPoint. There are separately licensed server technologies in Office System such as SharePoint. Microsoft shipped 2007 Office System to businesses on November 30, 2006 and to the general public January 30, 2007.
Other users said they just needed some time to get used to the ribbon. "It's becoming familiar," said Jen Glass, of developer OTB Solutions.
"[Fluent UI] is a change and it does require some adjustment but people very rapidly see the benefit in using the new UI," argued Microsoft's Alexieff. "It makes it easy to find commands, it makes it easy to use commands." Feedback is "overwhelmingly positive," he said.
No plans for changes to the ribbon
Microsoft currently is not planning any changes in Fluent UI to address concerns, Alexieff said. "We don't see enough concerns in the users who are deploying it to make any retrofitting kind of changes to it," he said.

redpajama_set_name: RedPajamaCommonCrawl


and time for a bit of me time. All's well in the world.
I've just had a conversation with my Mum and she's told me that my Dad needs some new eye drops to control his Glaucoma, these drops are vital to keep the pressure in his eye low enough and stop any more deterioration in his eyesight. Seems straightforward enough doesn't it. Apparently not, our local chemist can't get hold of the drops, there's a shortage! NO I'm sorry that's not good enough, my Dad needs those drops without them what's left of his sight will go and he will be blind!
It makes me so angry that ordinary good people face this lottery of treatment.
He's an old man he just wants to grow old gracefully and enjoy his time. Not be fretting about how long he can go without the correct medication.
My Lovely man has experienced the same problem with his Parkinson's medication, being told that the chemist cannot get his drugs for a few days because there's a shortage. What's going on? We have to make sure we have a good weeks worth of drugs in hand at all times. It's all just hassle. Bad enough for my Dad and Lovely Man, but what about people who have nobody to help them how the hell do they manage?
Sorry for the rant but to me it makes sense to spend the money on preventative or controlling medication, to keep people ticking along in their own homes, with the people they love for as long as possible. It's a shame that the powers that be don't seem to care about that.
I'm off to hug my family, do the same to yours, they're very special.
If I were you Id write to the medical directorate and your local MP. Its awful that people should have to wait for their meds. The moew people that kick up a stink and fight for what they should have the more, the higher powers i.e government will listen.
I had to fight for funding for my meds but I got there in the end as I refussed point blank to back down.
Your so right about family and friends they are so important life who be so lonely and empty without them.


There are many ways to contribute to The University of Texas School of Public Health. A gift may be in the form of cash, securities, real property or through various planned gift instruments. Planned gifts can provide steady income for the donor or a donor's family and can offer substantial tax advantages over a number of years.
Endowed gifts offer naming opportunities for the donor or someone the donor wishes to honor. They provide a dependable source of income that the school can use to renew and strengthen its greatest resources: a diverse and talented student body, outstanding faculty, exceptional educational programs, and excellence in community and laboratory research.
We are pleased to also offer the convenient option of online giving in areas that support the mission of the school.


Neotropical Region: French Guiana, Guyana (type locality), Suriname, Venezuela.
megalonyx. Cheliomyrmex megalonyx Wheeler, W.M. 1921d: 322, figs. 5, 9, 10 (s.w.m.) GUYANA. Wheeler, G.C. 1943: 320 (l.); Wheeler, G.C. & Wheeler, J. 1984: 267 (l.). See also: Borgmeier, 1955: 67.