Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/cc/base/cc_export.h b/cc/base/cc_export.h
new file mode 100644
index 0000000..fe0e0e3
--- /dev/null
+++ b/cc/base/cc_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_CC_EXPORT_H_
+#define CC_BASE_CC_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(CC_IMPLEMENTATION)
+#define CC_EXPORT __declspec(dllexport)
+#else
+#define CC_EXPORT __declspec(dllimport)
+#endif  // defined(CC_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(CC_IMPLEMENTATION)
+#define CC_EXPORT __attribute__((visibility("default")))
+#else
+#define CC_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define CC_EXPORT
+#endif
+
+#endif  // CC_BASE_CC_EXPORT_H_
diff --git a/cc/base/completion_event.h b/cc/base/completion_event.h
new file mode 100644
index 0000000..96ccf54
--- /dev/null
+++ b/cc/base/completion_event.h
@@ -0,0 +1,63 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_COMPLETION_EVENT_H_
+#define CC_BASE_COMPLETION_EVENT_H_
+
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace cc {
+
+// Used for making blocking calls from one thread to another. Use only when
+// absolutely certain that doing-so will not lead to a deadlock.
+//
+// It is safe to destroy this object as soon as Wait() returns.
+class CompletionEvent {
+ public:
+  CompletionEvent()
+      : event_(false /* manual_reset */, false /* initially_signaled */) {
+#if DCHECK_IS_ON
+    waited_ = false;
+    signaled_ = false;
+#endif
+  }
+
+  ~CompletionEvent() {
+#if DCHECK_IS_ON
+    DCHECK(waited_);
+    DCHECK(signaled_);
+#endif
+  }
+
+  void Wait() {
+#if DCHECK_IS_ON
+    DCHECK(!waited_);
+    waited_ = true;
+#endif
+    base::ThreadRestrictions::ScopedAllowWait allow_wait;
+    event_.Wait();
+  }
+
+  void Signal() {
+#if DCHECK_IS_ON
+    DCHECK(!signaled_);
+    signaled_ = true;
+#endif
+    event_.Signal();
+  }
+
+ private:
+  base::WaitableEvent event_;
+#if DCHECK_IS_ON
+  // Used to assert that Wait() and Signal() are each called exactly once.
+  bool waited_;
+  bool signaled_;
+#endif
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_COMPLETION_EVENT_H_
diff --git a/cc/base/delayed_unique_notifier.cc b/cc/base/delayed_unique_notifier.cc
new file mode 100644
index 0000000..c297267
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/delayed_unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+DelayedUniqueNotifier::DelayedUniqueNotifier(
+    base::SequencedTaskRunner* task_runner,
+    const base::Closure& closure,
+    const base::TimeDelta& delay)
+    : task_runner_(task_runner),
+      closure_(closure),
+      delay_(delay),
+      notification_pending_(false),
+      weak_ptr_factory_(this) {
+}
+
+DelayedUniqueNotifier::~DelayedUniqueNotifier() {
+}
+
+void DelayedUniqueNotifier::Schedule() {
+  if (notification_pending_) {
+    next_notification_time_ = Now() + delay_;
+    return;
+  }
+
+  next_notification_time_ = Now() + delay_;
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+                                           weak_ptr_factory_.GetWeakPtr()),
+                                delay_);
+  notification_pending_ = true;
+}
+
+void DelayedUniqueNotifier::Cancel() {
+  next_notification_time_ = base::TimeTicks();
+}
+
+bool DelayedUniqueNotifier::HasPendingNotification() const {
+  return notification_pending_ && !next_notification_time_.is_null();
+}
+
+base::TimeTicks DelayedUniqueNotifier::Now() const {
+  return base::TimeTicks::Now();
+}
+
+void DelayedUniqueNotifier::NotifyIfTime() {
+  // If next notifiaction time is not valid, then this schedule was canceled.
+  if (next_notification_time_.is_null()) {
+    notification_pending_ = false;
+    return;
+  }
+
+  // If the notification was rescheduled or arrived too early for any other
+  // reason, then post another task instead of running the callback.
+  base::TimeTicks now = Now();
+  if (next_notification_time_ > now) {
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+                   weak_ptr_factory_.GetWeakPtr()),
+        next_notification_time_ - now);
+    return;
+  }
+
+  // Note the order here is important since closure might schedule another run.
+  notification_pending_ = false;
+  closure_.Run();
+}
+
+}  // namespace cc
diff --git a/cc/base/delayed_unique_notifier.h b/cc/base/delayed_unique_notifier.h
new file mode 100644
index 0000000..1f93416
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
+#define CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace cc {
+
+class CC_EXPORT DelayedUniqueNotifier {
+ public:
+  // Configure this notifier to issue the |closure| notification in |delay| time
+  // from Schedule() call.
+  DelayedUniqueNotifier(base::SequencedTaskRunner* task_runner,
+                        const base::Closure& closure,
+                        const base::TimeDelta& delay);
+
+  // Destroying the notifier will ensure that no further notifications will
+  // happen from this class.
+  virtual ~DelayedUniqueNotifier();
+
+  // Schedule a notification to be run. If another notification is already
+  // pending, then it will happen in (at least) given delay from now. That is,
+  // if delay is 16ms and a notification has been scheduled 10ms ago (ie, it
+  // should trigger in no less than 6ms), then calling schedule will ensure that
+  // the only notification that arrives will happen in (at least) 16ms from now.
+  void Schedule();
+
+  // Cancel any previously scheduled runs.
+  void Cancel();
+
+  // Returns true if a notification is currently scheduled to run.
+  bool HasPendingNotification() const;
+
+ protected:
+  // Virtual for testing.
+  virtual base::TimeTicks Now() const;
+
+ private:
+  void NotifyIfTime();
+
+  base::SequencedTaskRunner* task_runner_;
+  base::Closure closure_;
+  base::TimeDelta delay_;
+  base::TimeTicks next_notification_time_;
+  bool notification_pending_;
+
+  base::WeakPtrFactory<DelayedUniqueNotifier> weak_ptr_factory_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/delayed_unique_notifier_unittest.cc b/cc/base/delayed_unique_notifier_unittest.cc
new file mode 100644
index 0000000..6c7ae6e
--- /dev/null
+++ b/cc/base/delayed_unique_notifier_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <deque>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/test_pending_task.h"
+#include "base/test/test_simple_task_runner.h"
+#include "cc/base/delayed_unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class TestNotifier : public DelayedUniqueNotifier {
+ public:
+  TestNotifier(base::SequencedTaskRunner* task_runner,
+               const base::Closure& closure,
+               const base::TimeDelta& delay)
+      : DelayedUniqueNotifier(task_runner, closure, delay) {}
+  virtual ~TestNotifier() {}
+
+  // Overridden from DelayedUniqueNotifier:
+  virtual base::TimeTicks Now() const OVERRIDE { return now_; }
+
+  void SetNow(base::TimeTicks now) { now_ = now; }
+
+ private:
+  base::TimeTicks now_;
+};
+
+class DelayedUniqueNotifierTest : public testing::Test {
+ public:
+  DelayedUniqueNotifierTest() : notification_count_(0) {}
+
+  virtual void SetUp() OVERRIDE {
+    notification_count_ = 0;
+    task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner);
+  }
+
+  void Notify() { ++notification_count_; }
+
+  int NotificationCount() const { return notification_count_; }
+
+  std::deque<base::TestPendingTask> TakePendingTasks() {
+    std::deque<base::TestPendingTask> tasks = task_runner_->GetPendingTasks();
+    task_runner_->ClearPendingTasks();
+    return tasks;
+  }
+
+ protected:
+  int notification_count_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(DelayedUniqueNotifierTest, ZeroDelay) {
+  base::TimeDelta delay = base::TimeDelta::FromInternalValue(0);
+  TestNotifier notifier(
+      task_runner_.get(),
+      base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+      delay);
+
+  EXPECT_EQ(0, NotificationCount());
+
+  // Basic schedule for |delay| from now.
+  base::TimeTicks schedule_time =
+      base::TimeTicks() + base::TimeDelta::FromInternalValue(10);
+
+  notifier.SetNow(schedule_time);
+  notifier.Schedule();
+
+  std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  tasks[0].task.Run();
+  EXPECT_EQ(1, NotificationCount());
+
+  // 5 schedules should result in only one run.
+  for (int i = 0; i < 5; ++i)
+    notifier.Schedule();
+
+  tasks = TakePendingTasks();
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  tasks[0].task.Run();
+  EXPECT_EQ(2, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, SmallDelay) {
+  base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+  TestNotifier notifier(
+      task_runner_.get(),
+      base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+      delay);
+
+  EXPECT_EQ(0, NotificationCount());
+
+  // Basic schedule for |delay| from now (now: 30, run time: 50).
+  base::TimeTicks schedule_time =
+      base::TimeTicks() + base::TimeDelta::FromInternalValue(30);
+
+  notifier.SetNow(schedule_time);
+  notifier.Schedule();
+
+  std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  // It's not yet time to run, so we expect no notifications.
+  tasks[0].task.Run();
+  EXPECT_EQ(0, NotificationCount());
+
+  tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  // Now the time should be delay minus whatever the value of now happens to be
+  // (now: 30, run time: 50).
+  base::TimeTicks scheduled_run_time = notifier.Now() + delay;
+  base::TimeTicks scheduled_delay =
+      base::TimeTicks() + (scheduled_run_time - notifier.Now());
+  EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+  // Move closer to the run time (time: 49, run time: 50).
+  notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(19));
+
+  // It's not yet time to run, so we expect no notifications.
+  tasks[0].task.Run();
+  EXPECT_EQ(0, NotificationCount());
+
+  tasks = TakePendingTasks();
+  ASSERT_EQ(1u, tasks.size());
+
+  // Now the time should be delay minus whatever the value of now happens to be.
+  scheduled_delay = base::TimeTicks() + (scheduled_run_time - notifier.Now());
+  EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+  // Move to exactly the run time (time: 50, run time: 50).
+  notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(1));
+
+  // It's time to run!
+  tasks[0].task.Run();
+  EXPECT_EQ(1, NotificationCount());
+
+  tasks = TakePendingTasks();
+  EXPECT_EQ(0u, tasks.size());
+}
+
+TEST_F(DelayedUniqueNotifierTest, RescheduleDelay) {
+  base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+  TestNotifier notifier(
+      task_runner_.get(),
+      base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+      delay);
+
+  base::TimeTicks schedule_time;
+  // Move time 19 units forward and reschedule, expecting that we still need to
+  // run in |delay| time and we don't get a notification.
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_EQ(0, NotificationCount());
+
+    // Move time forward 19 units.
+    schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(19);
+    notifier.SetNow(schedule_time);
+    notifier.Schedule();
+
+    std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+    ASSERT_EQ(1u, tasks.size());
+    EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+    // It's not yet time to run, so we expect no notifications.
+    tasks[0].task.Run();
+    EXPECT_EQ(0, NotificationCount());
+  }
+
+  // Move time forward 20 units, expecting a notification.
+  schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(20);
+  notifier.SetNow(schedule_time);
+
+  std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  // Time to run!
+  tasks[0].task.Run();
+  EXPECT_EQ(1, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, CancelAndHasPendingNotification) {
+  base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+  TestNotifier notifier(
+      task_runner_.get(),
+      base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+      delay);
+
+  EXPECT_EQ(0, NotificationCount());
+
+  // Schedule for |delay| seconds from now.
+  base::TimeTicks schedule_time =
+      notifier.Now() + base::TimeDelta::FromInternalValue(10);
+  notifier.SetNow(schedule_time);
+  notifier.Schedule();
+  EXPECT_TRUE(notifier.HasPendingNotification());
+
+  // Cancel the run.
+  notifier.Cancel();
+  EXPECT_FALSE(notifier.HasPendingNotification());
+
+  std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  // Time to run, but a canceled task!
+  tasks[0].task.Run();
+  EXPECT_EQ(0, NotificationCount());
+  EXPECT_FALSE(notifier.HasPendingNotification());
+
+  tasks = TakePendingTasks();
+  EXPECT_EQ(0u, tasks.size());
+
+  notifier.Schedule();
+  EXPECT_TRUE(notifier.HasPendingNotification());
+  tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  // Advance the time.
+  notifier.SetNow(notifier.Now() + delay);
+
+  // This should run since it wasn't canceled.
+  tasks[0].task.Run();
+  EXPECT_EQ(1, NotificationCount());
+  EXPECT_FALSE(notifier.HasPendingNotification());
+
+  for (int i = 0; i < 10; ++i) {
+    notifier.Schedule();
+    EXPECT_TRUE(notifier.HasPendingNotification());
+    notifier.Cancel();
+    EXPECT_FALSE(notifier.HasPendingNotification());
+  }
+
+  tasks = TakePendingTasks();
+
+  ASSERT_EQ(1u, tasks.size());
+  EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+  // Time to run, but a canceled task!
+  notifier.SetNow(notifier.Now() + delay);
+  tasks[0].task.Run();
+  EXPECT_EQ(1, NotificationCount());
+
+  tasks = TakePendingTasks();
+  EXPECT_EQ(0u, tasks.size());
+  EXPECT_FALSE(notifier.HasPendingNotification());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/float_quad_unittest.cc b/cc/base/float_quad_unittest.cc
new file mode 100644
index 0000000..c2186fd
--- /dev/null
+++ b/cc/base/float_quad_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/math_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+// TODO(danakj) Move this test to ui/gfx/ when we don't need MathUtil::MapQuad.
+TEST(FloatQuadTest, IsRectilinearTest) {
+  const int kNumRectilinear = 8;
+  gfx::Transform rectilinear_trans[kNumRectilinear];
+  rectilinear_trans[1].Rotate(90.f);
+  rectilinear_trans[2].Rotate(180.f);
+  rectilinear_trans[3].Rotate(270.f);
+  rectilinear_trans[4].SkewX(0.00000000001f);
+  rectilinear_trans[5].SkewY(0.00000000001f);
+  rectilinear_trans[6].Scale(0.00001f, 0.00001f);
+  rectilinear_trans[6].Rotate(180.f);
+  rectilinear_trans[7].Scale(100000.f, 100000.f);
+  rectilinear_trans[7].Rotate(180.f);
+
+  gfx::QuadF original(
+      gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f));
+
+  for (int i = 0; i < kNumRectilinear; ++i) {
+    bool clipped = false;
+    gfx::QuadF quad =
+        MathUtil::MapQuad(rectilinear_trans[i], original, &clipped);
+    ASSERT_TRUE(!clipped) << "case " << i;
+    EXPECT_TRUE(quad.IsRectilinear()) << "case " << i;
+  }
+
+  const int kNumNonRectilinear = 10;
+  gfx::Transform non_rectilinear_trans[kNumNonRectilinear];
+  non_rectilinear_trans[0].Rotate(359.9999f);
+  non_rectilinear_trans[1].Rotate(0.0000001f);
+  non_rectilinear_trans[2].Rotate(89.9999f);
+  non_rectilinear_trans[3].Rotate(90.00001f);
+  non_rectilinear_trans[4].Rotate(179.9999f);
+  non_rectilinear_trans[5].Rotate(180.00001f);
+  non_rectilinear_trans[6].Rotate(269.9999f);
+  non_rectilinear_trans[7].Rotate(270.0001f);
+  non_rectilinear_trans[8].SkewX(0.00001f);
+  non_rectilinear_trans[9].SkewY(0.00001f);
+
+  for (int i = 0; i < kNumNonRectilinear; ++i) {
+    bool clipped = false;
+    gfx::QuadF quad =
+        MathUtil::MapQuad(non_rectilinear_trans[i], original, &clipped);
+    ASSERT_TRUE(!clipped) << "case " << i;
+    EXPECT_FALSE(quad.IsRectilinear()) << "case " << i;
+  }
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/invalidation_region.cc b/cc/base/invalidation_region.cc
new file mode 100644
index 0000000..5fbca8d
--- /dev/null
+++ b/cc/base/invalidation_region.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/invalidation_region.h"
+
+#include "base/metrics/histogram.h"
+
+namespace {
+
+const int kMaxInvalidationRectCount = 256;
+
+}  // namespace
+
+namespace cc {
+
+InvalidationRegion::InvalidationRegion() {}
+
+InvalidationRegion::~InvalidationRegion() {}
+
+void InvalidationRegion::Swap(Region* region) {
+  region_.Swap(region);
+}
+
+void InvalidationRegion::Clear() {
+  region_.Clear();
+}
+
+void InvalidationRegion::Union(const gfx::Rect& rect) {
+  region_.Union(rect);
+  SimplifyIfNeeded();
+}
+
+void InvalidationRegion::SimplifyIfNeeded() {
+  if (region_.GetRegionComplexity() > kMaxInvalidationRectCount)
+    region_ = region_.bounds();
+}
+
+}  // namespace cc
diff --git a/cc/base/invalidation_region.h b/cc/base/invalidation_region.h
new file mode 100644
index 0000000..9cb2fe3
--- /dev/null
+++ b/cc/base/invalidation_region.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_INVALIDATION_REGION_H_
+#define CC_BASE_INVALIDATION_REGION_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/base/region.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+
+// This class behaves similarly to Region, but it may have false positives. That
+// is, InvalidationRegion can be simplified to encompass a larger area than the
+// collection of rects unioned.
+class CC_EXPORT InvalidationRegion {
+ public:
+  InvalidationRegion();
+  ~InvalidationRegion();
+
+  void Swap(Region* region);
+  void Clear();
+  void Union(const gfx::Rect& rect);
+  bool IsEmpty() const { return region_.IsEmpty(); }
+
+ private:
+  void SimplifyIfNeeded();
+
+  Region region_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_INVALIDATION_REGION_H_
diff --git a/cc/base/latency_info_swap_promise.cc b/cc/base/latency_info_swap_promise.cc
new file mode 100644
index 0000000..8c6ee0e
--- /dev/null
+++ b/cc/base/latency_info_swap_promise.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/latency_info_swap_promise.h"
+
+#include "base/logging.h"
+
+namespace {
+  ui::LatencyComponentType DidNotSwapReasonToLatencyComponentType(
+      cc::SwapPromise::DidNotSwapReason reason) {
+    switch (reason) {
+      case cc::SwapPromise::DID_NOT_SWAP_UNKNOWN:
+      case cc::SwapPromise::SWAP_FAILS:
+        return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT;
+      case cc::SwapPromise::COMMIT_FAILS:
+        return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_FAILED_COMPONENT;
+      case cc::SwapPromise::COMMIT_NO_UPDATE:
+        return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_NO_UPDATE_COMPONENT;
+    }
+    NOTREACHED() << "Unhandled DidNotSwapReason.";
+    return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT;
+  }
+}  // namespace
+
+namespace cc {
+
+LatencyInfoSwapPromise::LatencyInfoSwapPromise(const ui::LatencyInfo& latency)
+    : latency_(latency) {
+}
+
+LatencyInfoSwapPromise::~LatencyInfoSwapPromise() {
+}
+
+void LatencyInfoSwapPromise::DidSwap(CompositorFrameMetadata* metadata) {
+  DCHECK(!latency_.terminated);
+  metadata->latency_info.push_back(latency_);
+}
+
+void LatencyInfoSwapPromise::DidNotSwap(DidNotSwapReason reason) {
+  latency_.AddLatencyNumber(DidNotSwapReasonToLatencyComponentType(reason),
+                            0, 0);
+  // TODO(miletus): Turn this back on once per-event LatencyInfo tracking
+  // is enabled in GPU side.
+  // DCHECK(latency_.terminated);
+}
+
+int64 LatencyInfoSwapPromise::TraceId() const {
+  return latency_.trace_id;
+}
+
+}  // namespace cc
diff --git a/cc/base/latency_info_swap_promise.h b/cc/base/latency_info_swap_promise.h
new file mode 100644
index 0000000..a210a3f
--- /dev/null
+++ b/cc/base/latency_info_swap_promise.h
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_LATENCY_INFO_SWAP_PROMISE_H_
+#define CC_BASE_LATENCY_INFO_SWAP_PROMISE_H_
+
+#include "base/compiler_specific.h"
+#include "cc/base/swap_promise.h"
+#include "ui/events/latency_info.h"
+
+namespace cc {
+
+class CC_EXPORT LatencyInfoSwapPromise : public SwapPromise {
+ public:
+  explicit LatencyInfoSwapPromise(const ui::LatencyInfo& latency_info);
+  virtual ~LatencyInfoSwapPromise();
+
+  virtual void DidSwap(CompositorFrameMetadata* metadata) OVERRIDE;
+  virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE;
+
+  virtual int64 TraceId() const OVERRIDE;
+
+ private:
+  ui::LatencyInfo latency_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_LATENCY_INFO_SWAP_PROMISE_H_
diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc
new file mode 100644
index 0000000..f724d6c
--- /dev/null
+++ b/cc/base/latency_info_swap_promise_monitor.cc
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/latency_info_swap_promise_monitor.h"
+
+#include "base/threading/platform_thread.h"
+#include "cc/base/latency_info_swap_promise.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace {
+
+bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) {
+  if (latency_info->FindLatency(
+          ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL))
+    return false;
+  latency_info->AddLatencyNumber(
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0);
+  return true;
+}
+
+bool AddForwardingScrollUpdateToMainComponent(ui::LatencyInfo* latency_info) {
+  if (latency_info->FindLatency(
+          ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
+          0,
+          NULL))
+    return false;
+  latency_info->AddLatencyNumber(
+      ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
+      0,
+      latency_info->trace_id);
+  return true;
+}
+
+}  // namespace
+
+namespace cc {
+
+LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor(
+    ui::LatencyInfo* latency,
+    LayerTreeHost* layer_tree_host,
+    LayerTreeHostImpl* layer_tree_host_impl)
+    : SwapPromiseMonitor(layer_tree_host, layer_tree_host_impl),
+      latency_(latency) {}
+
+LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() {}
+
+void LatencyInfoSwapPromiseMonitor::OnSetNeedsCommitOnMain() {
+  if (AddRenderingScheduledComponent(latency_)) {
+    scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
+    layer_tree_host_->QueueSwapPromise(swap_promise.Pass());
+  }
+}
+
+void LatencyInfoSwapPromiseMonitor::OnSetNeedsRedrawOnImpl() {
+  if (AddRenderingScheduledComponent(latency_)) {
+    scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
+    layer_tree_host_impl_->active_tree()->QueueSwapPromise(swap_promise.Pass());
+  }
+}
+
+void LatencyInfoSwapPromiseMonitor::OnForwardScrollUpdateToMainThreadOnImpl() {
+  if (AddForwardingScrollUpdateToMainComponent(latency_)) {
+    int64 new_sequence_number = 0;
+    for (ui::LatencyInfo::LatencyMap::const_iterator it =
+             latency_->latency_components.begin();
+         it != latency_->latency_components.end();
+         ++it) {
+      if (it->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT) {
+        new_sequence_number =
+            (static_cast<int64>(base::PlatformThread::CurrentId()) << 32) |
+            (it->second.sequence_number & 0xffffffff);
+        DCHECK(new_sequence_number != it->second.sequence_number);
+        break;
+      }
+    }
+    if (!new_sequence_number)
+      return;
+    ui::LatencyInfo new_latency;
+    new_latency.AddLatencyNumber(
+        ui::INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT,
+        0,
+        new_sequence_number);
+    new_latency.TraceEventType("ScrollUpdate");
+    new_latency.CopyLatencyFrom(
+        *latency_,
+        ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT);
+    scoped_ptr<SwapPromise> swap_promise(
+        new LatencyInfoSwapPromise(new_latency));
+    layer_tree_host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(
+        swap_promise.Pass());
+  }
+}
+
+}  // namespace cc
diff --git a/cc/base/latency_info_swap_promise_monitor.h b/cc/base/latency_info_swap_promise_monitor.h
new file mode 100644
index 0000000..1a114fb
--- /dev/null
+++ b/cc/base/latency_info_swap_promise_monitor.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "cc/base/swap_promise_monitor.h"
+
+#ifndef CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
+#define CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
+
+namespace ui {
+struct LatencyInfo;
+}  // namespace ui
+
+namespace cc {
+
+// A LatencyInfoSwapPromiseMonitor queues a LatencyInfoSwapPromise into
+// LayerTreeHost or LayerTreeHostImpl if there is compositor state change
+// while it is being mointored.
+class CC_EXPORT LatencyInfoSwapPromiseMonitor : public SwapPromiseMonitor {
+ public:
+  LatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency,
+                                LayerTreeHost* layer_tree_host,
+                                LayerTreeHostImpl* layer_tree_host_impl);
+  virtual ~LatencyInfoSwapPromiseMonitor();
+
+  virtual void OnSetNeedsCommitOnMain() OVERRIDE;
+  virtual void OnSetNeedsRedrawOnImpl() OVERRIDE;
+  virtual void OnForwardScrollUpdateToMainThreadOnImpl() OVERRIDE;
+
+ private:
+  ui::LatencyInfo* latency_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
new file mode 100644
index 0000000..5df1e56
--- /dev/null
+++ b/cc/base/math_util.cc
@@ -0,0 +1,834 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/math_util.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "base/debug/trace_event_argument.h"
+#include "base/values.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+#include "ui/gfx/vector2d_f.h"
+
+namespace cc {
+
+const double MathUtil::kPiDouble = 3.14159265358979323846;
+const float MathUtil::kPiFloat = 3.14159265358979323846f;
+
+static HomogeneousCoordinate ProjectHomogeneousPoint(
+    const gfx::Transform& transform,
+    const gfx::PointF& p) {
+  // In this case, the layer we are trying to project onto is perpendicular to
+  // ray (point p and z-axis direction) that we are trying to project. This
+  // happens when the layer is rotated so that it is infinitesimally thin, or
+  // when it is co-planar with the camera origin -- i.e. when the layer is
+  // invisible anyway.
+  if (!transform.matrix().get(2, 2))
+    return HomogeneousCoordinate(0.0, 0.0, 0.0, 1.0);
+
+  SkMScalar z = -(transform.matrix().get(2, 0) * p.x() +
+             transform.matrix().get(2, 1) * p.y() +
+             transform.matrix().get(2, 3)) /
+             transform.matrix().get(2, 2);
+  HomogeneousCoordinate result(p.x(), p.y(), z, 1.0);
+  transform.matrix().mapMScalars(result.vec, result.vec);
+  return result;
+}
+
+static HomogeneousCoordinate ProjectHomogeneousPoint(
+    const gfx::Transform& transform,
+    const gfx::PointF& p,
+    bool* clipped) {
+  HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p);
+  *clipped = h.w() <= 0;
+  return h;
+}
+
+static HomogeneousCoordinate MapHomogeneousPoint(
+    const gfx::Transform& transform,
+    const gfx::Point3F& p) {
+  HomogeneousCoordinate result(p.x(), p.y(), p.z(), 1.0);
+  transform.matrix().mapMScalars(result.vec, result.vec);
+  return result;
+}
+
+static HomogeneousCoordinate ComputeClippedPointForEdge(
+    const HomogeneousCoordinate& h1,
+    const HomogeneousCoordinate& h2) {
+  // Points h1 and h2 form a line in 4d, and any point on that line can be
+  // represented as an interpolation between h1 and h2:
+  //    p = (1-t) h1 + (t) h2
+  //
+  // We want to compute point p such that p.w == epsilon, where epsilon is a
+  // small non-zero number. (but the smaller the number is, the higher the risk
+  // of overflow)
+  // To do this, we solve for t in the following equation:
+  //    p.w = epsilon = (1-t) * h1.w + (t) * h2.w
+  //
+  // Once paramter t is known, the rest of p can be computed via
+  //    p = (1-t) h1 + (t) h2.
+
+  // Technically this is a special case of the following assertion, but its a
+  // good idea to keep it an explicit sanity check here.
+  DCHECK_NE(h2.w(), h1.w());
+  // Exactly one of h1 or h2 (but not both) must be on the negative side of the
+  // w plane when this is called.
+  DCHECK(h1.ShouldBeClipped() ^ h2.ShouldBeClipped());
+
+  // ...or any positive non-zero small epsilon
+  SkMScalar w = 0.00001f;
+  SkMScalar t = (w - h1.w()) / (h2.w() - h1.w());
+
+  SkMScalar x = (SK_MScalar1 - t) * h1.x() + t * h2.x();
+  SkMScalar y = (SK_MScalar1 - t) * h1.y() + t * h2.y();
+  SkMScalar z = (SK_MScalar1 - t) * h1.z() + t * h2.z();
+
+  return HomogeneousCoordinate(x, y, z, w);
+}
+
+static inline void ExpandBoundsToIncludePoint(float* xmin,
+                                              float* xmax,
+                                              float* ymin,
+                                              float* ymax,
+                                              const gfx::PointF& p) {
+  *xmin = std::min(p.x(), *xmin);
+  *xmax = std::max(p.x(), *xmax);
+  *ymin = std::min(p.y(), *ymin);
+  *ymax = std::max(p.y(), *ymax);
+}
+
+static inline void AddVertexToClippedQuad(const gfx::PointF& new_vertex,
+                                          gfx::PointF clipped_quad[8],
+                                          int* num_vertices_in_clipped_quad) {
+  clipped_quad[*num_vertices_in_clipped_quad] = new_vertex;
+  (*num_vertices_in_clipped_quad)++;
+}
+
+static inline void AddVertexToClippedQuad3d(const gfx::Point3F& new_vertex,
+                                            gfx::Point3F clipped_quad[8],
+                                            int* num_vertices_in_clipped_quad) {
+  clipped_quad[*num_vertices_in_clipped_quad] = new_vertex;
+  (*num_vertices_in_clipped_quad)++;
+}
+
+gfx::Rect MathUtil::MapEnclosingClippedRect(const gfx::Transform& transform,
+                                            const gfx::Rect& src_rect) {
+  if (transform.IsIdentityOrIntegerTranslation()) {
+    gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+                         static_cast<int>(transform.matrix().getFloat(1, 3)));
+    return src_rect + offset;
+  }
+  return gfx::ToEnclosingRect(MapClippedRect(transform, gfx::RectF(src_rect)));
+}
+
+gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform,
+                                    const gfx::RectF& src_rect) {
+  if (transform.IsIdentityOrTranslation()) {
+    gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+                          transform.matrix().getFloat(1, 3));
+    return src_rect + offset;
+  }
+
+  // Apply the transform, but retain the result in homogeneous coordinates.
+
+  SkMScalar quad[4 * 2];  // input: 4 x 2D points
+  quad[0] = src_rect.x();
+  quad[1] = src_rect.y();
+  quad[2] = src_rect.right();
+  quad[3] = src_rect.y();
+  quad[4] = src_rect.right();
+  quad[5] = src_rect.bottom();
+  quad[6] = src_rect.x();
+  quad[7] = src_rect.bottom();
+
+  SkMScalar result[4 * 4];  // output: 4 x 4D homogeneous points
+  transform.matrix().map2(quad, 4, result);
+
+  HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
+  HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
+  HomogeneousCoordinate hc2(result[8], result[9], result[10], result[11]);
+  HomogeneousCoordinate hc3(result[12], result[13], result[14], result[15]);
+  return ComputeEnclosingClippedRect(hc0, hc1, hc2, hc3);
+}
+
+gfx::Rect MathUtil::ProjectEnclosingClippedRect(const gfx::Transform& transform,
+                                                const gfx::Rect& src_rect) {
+  if (transform.IsIdentityOrIntegerTranslation()) {
+    gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+                         static_cast<int>(transform.matrix().getFloat(1, 3)));
+    return src_rect + offset;
+  }
+  return gfx::ToEnclosingRect(
+      ProjectClippedRect(transform, gfx::RectF(src_rect)));
+}
+
+gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform,
+                                        const gfx::RectF& src_rect) {
+  if (transform.IsIdentityOrTranslation()) {
+    gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+                          transform.matrix().getFloat(1, 3));
+    return src_rect + offset;
+  }
+
+  // Perform the projection, but retain the result in homogeneous coordinates.
+  gfx::QuadF q = gfx::QuadF(src_rect);
+  HomogeneousCoordinate h1 = ProjectHomogeneousPoint(transform, q.p1());
+  HomogeneousCoordinate h2 = ProjectHomogeneousPoint(transform, q.p2());
+  HomogeneousCoordinate h3 = ProjectHomogeneousPoint(transform, q.p3());
+  HomogeneousCoordinate h4 = ProjectHomogeneousPoint(transform, q.p4());
+
+  return ComputeEnclosingClippedRect(h1, h2, h3, h4);
+}
+
+gfx::Rect MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
+    const gfx::Transform& transform,
+    const gfx::Rect& rect) {
+  DCHECK(transform.Preserves2dAxisAlignment());
+
+  if (transform.IsIdentityOrIntegerTranslation()) {
+    gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+                         static_cast<int>(transform.matrix().getFloat(1, 3)));
+    return rect + offset;
+  }
+  if (transform.IsIdentityOrTranslation()) {
+    gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+                          transform.matrix().getFloat(1, 3));
+    return gfx::ToEnclosedRect(rect + offset);
+  }
+
+  SkMScalar quad[2 * 2];  // input: 2 x 2D points
+  quad[0] = rect.x();
+  quad[1] = rect.y();
+  quad[2] = rect.right();
+  quad[3] = rect.bottom();
+
+  SkMScalar result[4 * 2];  // output: 2 x 4D homogeneous points
+  transform.matrix().map2(quad, 2, result);
+
+  HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
+  HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
+  DCHECK(!hc0.ShouldBeClipped());
+  DCHECK(!hc1.ShouldBeClipped());
+
+  gfx::PointF top_left(hc0.CartesianPoint2d());
+  gfx::PointF bottom_right(hc1.CartesianPoint2d());
+  return gfx::ToEnclosedRect(gfx::BoundingRect(top_left, bottom_right));
+}
+
+void MathUtil::MapClippedQuad(const gfx::Transform& transform,
+                              const gfx::QuadF& src_quad,
+                              gfx::PointF clipped_quad[8],
+                              int* num_vertices_in_clipped_quad) {
+  HomogeneousCoordinate h1 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1()));
+  HomogeneousCoordinate h2 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2()));
+  HomogeneousCoordinate h3 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3()));
+  HomogeneousCoordinate h4 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4()));
+
+  // The order of adding the vertices to the array is chosen so that
+  // clockwise / counter-clockwise orientation is retained.
+
+  *num_vertices_in_clipped_quad = 0;
+
+  if (!h1.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        h1.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        ComputeClippedPointForEdge(h1, h2).CartesianPoint2d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h2.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        h2.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        ComputeClippedPointForEdge(h2, h3).CartesianPoint2d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h3.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        h3.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        ComputeClippedPointForEdge(h3, h4).CartesianPoint2d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h4.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        h4.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) {
+    AddVertexToClippedQuad(
+        ComputeClippedPointForEdge(h4, h1).CartesianPoint2d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  DCHECK_LE(*num_vertices_in_clipped_quad, 8);
+}
+
+bool MathUtil::MapClippedQuad3d(const gfx::Transform& transform,
+                                const gfx::QuadF& src_quad,
+                                gfx::Point3F clipped_quad[8],
+                                int* num_vertices_in_clipped_quad) {
+  HomogeneousCoordinate h1 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1()));
+  HomogeneousCoordinate h2 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2()));
+  HomogeneousCoordinate h3 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3()));
+  HomogeneousCoordinate h4 =
+      MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4()));
+
+  // The order of adding the vertices to the array is chosen so that
+  // clockwise / counter-clockwise orientation is retained.
+
+  *num_vertices_in_clipped_quad = 0;
+
+  if (!h1.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        h1.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        ComputeClippedPointForEdge(h1, h2).CartesianPoint3d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h2.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        h2.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        ComputeClippedPointForEdge(h2, h3).CartesianPoint3d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h3.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        h3.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        ComputeClippedPointForEdge(h3, h4).CartesianPoint3d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  if (!h4.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        h4.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+  }
+
+  if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) {
+    AddVertexToClippedQuad3d(
+        ComputeClippedPointForEdge(h4, h1).CartesianPoint3d(),
+        clipped_quad,
+        num_vertices_in_clipped_quad);
+  }
+
+  DCHECK_LE(*num_vertices_in_clipped_quad, 8);
+  return (*num_vertices_in_clipped_quad >= 4);
+}
+
+gfx::RectF MathUtil::ComputeEnclosingRectOfVertices(
+    const gfx::PointF vertices[],
+    int num_vertices) {
+  if (num_vertices < 2)
+    return gfx::RectF();
+
+  float xmin = std::numeric_limits<float>::max();
+  float xmax = -std::numeric_limits<float>::max();
+  float ymin = std::numeric_limits<float>::max();
+  float ymax = -std::numeric_limits<float>::max();
+
+  for (int i = 0; i < num_vertices; ++i)
+    ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, vertices[i]);
+
+  return gfx::RectF(gfx::PointF(xmin, ymin),
+                    gfx::SizeF(xmax - xmin, ymax - ymin));
+}
+
+gfx::RectF MathUtil::ComputeEnclosingClippedRect(
+    const HomogeneousCoordinate& h1,
+    const HomogeneousCoordinate& h2,
+    const HomogeneousCoordinate& h3,
+    const HomogeneousCoordinate& h4) {
+  // This function performs clipping as necessary and computes the enclosing 2d
+  // gfx::RectF of the vertices. Doing these two steps simultaneously allows us
+  // to avoid the overhead of storing an unknown number of clipped vertices.
+
+  // If no vertices on the quad are clipped, then we can simply return the
+  // enclosing rect directly.
+  bool something_clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+                           h3.ShouldBeClipped() || h4.ShouldBeClipped();
+  if (!something_clipped) {
+    gfx::QuadF mapped_quad = gfx::QuadF(h1.CartesianPoint2d(),
+                                        h2.CartesianPoint2d(),
+                                        h3.CartesianPoint2d(),
+                                        h4.CartesianPoint2d());
+    return mapped_quad.BoundingBox();
+  }
+
+  bool everything_clipped = h1.ShouldBeClipped() && h2.ShouldBeClipped() &&
+                            h3.ShouldBeClipped() && h4.ShouldBeClipped();
+  if (everything_clipped)
+    return gfx::RectF();
+
+  float xmin = std::numeric_limits<float>::max();
+  float xmax = -std::numeric_limits<float>::max();
+  float ymin = std::numeric_limits<float>::max();
+  float ymax = -std::numeric_limits<float>::max();
+
+  if (!h1.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+                               h1.CartesianPoint2d());
+
+  if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin,
+                               &xmax,
+                               &ymin,
+                               &ymax,
+                               ComputeClippedPointForEdge(h1, h2)
+                                   .CartesianPoint2d());
+
+  if (!h2.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+                               h2.CartesianPoint2d());
+
+  if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin,
+                               &xmax,
+                               &ymin,
+                               &ymax,
+                               ComputeClippedPointForEdge(h2, h3)
+                                   .CartesianPoint2d());
+
+  if (!h3.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+                               h3.CartesianPoint2d());
+
+  if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin,
+                               &xmax,
+                               &ymin,
+                               &ymax,
+                               ComputeClippedPointForEdge(h3, h4)
+                                   .CartesianPoint2d());
+
+  if (!h4.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+                               h4.CartesianPoint2d());
+
+  if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped())
+    ExpandBoundsToIncludePoint(&xmin,
+                               &xmax,
+                               &ymin,
+                               &ymax,
+                               ComputeClippedPointForEdge(h4, h1)
+                                   .CartesianPoint2d());
+
+  return gfx::RectF(gfx::PointF(xmin, ymin),
+                    gfx::SizeF(xmax - xmin, ymax - ymin));
+}
+
+gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform,
+                             const gfx::QuadF& q,
+                             bool* clipped) {
+  if (transform.IsIdentityOrTranslation()) {
+    gfx::QuadF mapped_quad(q);
+    mapped_quad += gfx::Vector2dF(transform.matrix().getFloat(0, 3),
+                                  transform.matrix().getFloat(1, 3));
+    *clipped = false;
+    return mapped_quad;
+  }
+
+  HomogeneousCoordinate h1 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p1()));
+  HomogeneousCoordinate h2 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p2()));
+  HomogeneousCoordinate h3 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p3()));
+  HomogeneousCoordinate h4 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p4()));
+
+  *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+            h3.ShouldBeClipped() || h4.ShouldBeClipped();
+
+  // Result will be invalid if clipped == true. But, compute it anyway just in
+  // case, to emulate existing behavior.
+  return gfx::QuadF(h1.CartesianPoint2d(),
+                    h2.CartesianPoint2d(),
+                    h3.CartesianPoint2d(),
+                    h4.CartesianPoint2d());
+}
+
+gfx::QuadF MathUtil::MapQuad3d(const gfx::Transform& transform,
+                               const gfx::QuadF& q,
+                               gfx::Point3F* p,
+                               bool* clipped) {
+  if (transform.IsIdentityOrTranslation()) {
+    gfx::QuadF mapped_quad(q);
+    mapped_quad += gfx::Vector2dF(transform.matrix().getFloat(0, 3),
+                                  transform.matrix().getFloat(1, 3));
+    *clipped = false;
+    p[0] = gfx::Point3F(mapped_quad.p1().x(), mapped_quad.p1().y(), 0.0f);
+    p[1] = gfx::Point3F(mapped_quad.p2().x(), mapped_quad.p2().y(), 0.0f);
+    p[2] = gfx::Point3F(mapped_quad.p3().x(), mapped_quad.p3().y(), 0.0f);
+    p[3] = gfx::Point3F(mapped_quad.p4().x(), mapped_quad.p4().y(), 0.0f);
+    return mapped_quad;
+  }
+
+  HomogeneousCoordinate h1 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p1()));
+  HomogeneousCoordinate h2 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p2()));
+  HomogeneousCoordinate h3 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p3()));
+  HomogeneousCoordinate h4 =
+      MapHomogeneousPoint(transform, gfx::Point3F(q.p4()));
+
+  *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+             h3.ShouldBeClipped() || h4.ShouldBeClipped();
+
+  // Result will be invalid if clipped == true. But, compute it anyway just in
+  // case, to emulate existing behavior.
+  p[0] = h1.CartesianPoint3d();
+  p[1] = h2.CartesianPoint3d();
+  p[2] = h3.CartesianPoint3d();
+  p[3] = h4.CartesianPoint3d();
+
+  return gfx::QuadF(h1.CartesianPoint2d(),
+                    h2.CartesianPoint2d(),
+                    h3.CartesianPoint2d(),
+                    h4.CartesianPoint2d());
+}
+
+gfx::PointF MathUtil::MapPoint(const gfx::Transform& transform,
+                               const gfx::PointF& p,
+                               bool* clipped) {
+  HomogeneousCoordinate h = MapHomogeneousPoint(transform, gfx::Point3F(p));
+
+  if (h.w() > 0) {
+    *clipped = false;
+    return h.CartesianPoint2d();
+  }
+
+  // The cartesian coordinates will be invalid after dividing by w.
+  *clipped = true;
+
+  // Avoid dividing by w if w == 0.
+  if (!h.w())
+    return gfx::PointF();
+
+  // This return value will be invalid because clipped == true, but (1) users of
+  // this code should be ignoring the return value when clipped == true anyway,
+  // and (2) this behavior is more consistent with existing behavior of WebKit
+  // transforms if the user really does not ignore the return value.
+  return h.CartesianPoint2d();
+}
+
+gfx::Point3F MathUtil::MapPoint(const gfx::Transform& transform,
+                                const gfx::Point3F& p,
+                                bool* clipped) {
+  HomogeneousCoordinate h = MapHomogeneousPoint(transform, p);
+
+  if (h.w() > 0) {
+    *clipped = false;
+    return h.CartesianPoint3d();
+  }
+
+  // The cartesian coordinates will be invalid after dividing by w.
+  *clipped = true;
+
+  // Avoid dividing by w if w == 0.
+  if (!h.w())
+    return gfx::Point3F();
+
+  // This return value will be invalid because clipped == true, but (1) users of
+  // this code should be ignoring the return value when clipped == true anyway,
+  // and (2) this behavior is more consistent with existing behavior of WebKit
+  // transforms if the user really does not ignore the return value.
+  return h.CartesianPoint3d();
+}
+
+gfx::QuadF MathUtil::ProjectQuad(const gfx::Transform& transform,
+                                 const gfx::QuadF& q,
+                                 bool* clipped) {
+  gfx::QuadF projected_quad;
+  bool clipped_point;
+  projected_quad.set_p1(ProjectPoint(transform, q.p1(), &clipped_point));
+  *clipped = clipped_point;
+  projected_quad.set_p2(ProjectPoint(transform, q.p2(), &clipped_point));
+  *clipped |= clipped_point;
+  projected_quad.set_p3(ProjectPoint(transform, q.p3(), &clipped_point));
+  *clipped |= clipped_point;
+  projected_quad.set_p4(ProjectPoint(transform, q.p4(), &clipped_point));
+  *clipped |= clipped_point;
+
+  return projected_quad;
+}
+
+gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform,
+                                   const gfx::PointF& p,
+                                   bool* clipped) {
+  HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p, clipped);
+  // Avoid dividing by w if w == 0.
+  if (!h.w())
+    return gfx::PointF();
+
+  // This return value will be invalid if clipped == true, but (1) users of
+  // this code should be ignoring the return value when clipped == true anyway,
+  // and (2) this behavior is more consistent with existing behavior of WebKit
+  // transforms if the user really does not ignore the return value.
+  return h.CartesianPoint2d();
+}
+
+gfx::Point3F MathUtil::ProjectPoint3D(const gfx::Transform& transform,
+                                      const gfx::PointF& p,
+                                      bool* clipped) {
+  HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p, clipped);
+  if (!h.w())
+    return gfx::Point3F();
+  return h.CartesianPoint3d();
+}
+
+gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect,
+                                           const gfx::RectF& scale_outer_rect,
+                                           const gfx::RectF& scale_inner_rect) {
+  gfx::RectF output_inner_rect = input_outer_rect;
+  float scale_rect_to_input_scale_x =
+      scale_outer_rect.width() / input_outer_rect.width();
+  float scale_rect_to_input_scale_y =
+      scale_outer_rect.height() / input_outer_rect.height();
+
+  gfx::Vector2dF top_left_diff =
+      scale_inner_rect.origin() - scale_outer_rect.origin();
+  gfx::Vector2dF bottom_right_diff =
+      scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right();
+  output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x,
+                          top_left_diff.y() / scale_rect_to_input_scale_y,
+                          -bottom_right_diff.x() / scale_rect_to_input_scale_x,
+                          -bottom_right_diff.y() / scale_rect_to_input_scale_y);
+  return output_inner_rect;
+}
+
+static inline bool NearlyZero(double value) {
+  return std::abs(value) < std::numeric_limits<double>::epsilon();
+}
+
+static inline float ScaleOnAxis(double a, double b, double c) {
+  if (NearlyZero(b) && NearlyZero(c))
+    return std::abs(a);
+  if (NearlyZero(a) && NearlyZero(c))
+    return std::abs(b);
+  if (NearlyZero(a) && NearlyZero(b))
+    return std::abs(c);
+
+  // Do the sqrt as a double to not lose precision.
+  return static_cast<float>(std::sqrt(a * a + b * b + c * c));
+}
+
+gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents(
+    const gfx::Transform& transform,
+    float fallback_value) {
+  if (transform.HasPerspective())
+    return gfx::Vector2dF(fallback_value, fallback_value);
+  float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0),
+                              transform.matrix().getDouble(1, 0),
+                              transform.matrix().getDouble(2, 0));
+  float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1),
+                              transform.matrix().getDouble(1, 1),
+                              transform.matrix().getDouble(2, 1));
+  return gfx::Vector2dF(x_scale, y_scale);
+}
+
+float MathUtil::SmallestAngleBetweenVectors(const gfx::Vector2dF& v1,
+                                            const gfx::Vector2dF& v2) {
+  double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length();
+  // Clamp to compensate for rounding errors.
+  dot_product = std::max(-1.0, std::min(1.0, dot_product));
+  return static_cast<float>(Rad2Deg(std::acos(dot_product)));
+}
+
+gfx::Vector2dF MathUtil::ProjectVector(const gfx::Vector2dF& source,
+                                       const gfx::Vector2dF& destination) {
+  float projected_length =
+      gfx::DotProduct(source, destination) / destination.LengthSquared();
+  return gfx::Vector2dF(projected_length * destination.x(),
+                        projected_length * destination.y());
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Size& s) {
+  scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
+  res->SetDouble("width", s.width());
+  res->SetDouble("height", s.height());
+  return res.Pass();
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Rect& r) {
+  scoped_ptr<base::ListValue> res(new base::ListValue());
+  res->AppendInteger(r.x());
+  res->AppendInteger(r.y());
+  res->AppendInteger(r.width());
+  res->AppendInteger(r.height());
+  return res.Pass();
+}
+
+bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) {
+  const base::ListValue* value = NULL;
+  if (!raw_value->GetAsList(&value))
+    return false;
+
+  if (value->GetSize() != 4)
+    return false;
+
+  int x, y, w, h;
+  bool ok = true;
+  ok &= value->GetInteger(0, &x);
+  ok &= value->GetInteger(1, &y);
+  ok &= value->GetInteger(2, &w);
+  ok &= value->GetInteger(3, &h);
+  if (!ok)
+    return false;
+
+  *out_rect = gfx::Rect(x, y, w, h);
+  return true;
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::PointF& pt) {
+  scoped_ptr<base::ListValue> res(new base::ListValue());
+  res->AppendDouble(pt.x());
+  res->AppendDouble(pt.y());
+  return res.Pass();
+}
+
+void MathUtil::AddToTracedValue(const gfx::Size& s,
+                                base::debug::TracedValue* res) {
+  res->SetDouble("width", s.width());
+  res->SetDouble("height", s.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::SizeF& s,
+                                base::debug::TracedValue* res) {
+  res->SetDouble("width", s.width());
+  res->SetDouble("height", s.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Rect& r,
+                                base::debug::TracedValue* res) {
+  res->AppendInteger(r.x());
+  res->AppendInteger(r.y());
+  res->AppendInteger(r.width());
+  res->AppendInteger(r.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::PointF& pt,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(pt.x());
+  res->AppendDouble(pt.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Point3F& pt,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(pt.x());
+  res->AppendDouble(pt.y());
+  res->AppendDouble(pt.z());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Vector2d& v,
+                                base::debug::TracedValue* res) {
+  res->AppendInteger(v.x());
+  res->AppendInteger(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Vector2dF& v,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(v.x());
+  res->AppendDouble(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::ScrollOffset& v,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(v.x());
+  res->AppendDouble(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::QuadF& q,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(q.p1().x());
+  res->AppendDouble(q.p1().y());
+  res->AppendDouble(q.p2().x());
+  res->AppendDouble(q.p2().y());
+  res->AppendDouble(q.p3().x());
+  res->AppendDouble(q.p3().y());
+  res->AppendDouble(q.p4().x());
+  res->AppendDouble(q.p4().y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::RectF& rect,
+                                base::debug::TracedValue* res) {
+  res->AppendDouble(rect.x());
+  res->AppendDouble(rect.y());
+  res->AppendDouble(rect.width());
+  res->AppendDouble(rect.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Transform& transform,
+                                base::debug::TracedValue* res) {
+  const SkMatrix44& m = transform.matrix();
+  for (int row = 0; row < 4; ++row) {
+    for (int col = 0; col < 4; ++col)
+      res->AppendDouble(m.getDouble(row, col));
+  }
+}
+
+void MathUtil::AddToTracedValue(const gfx::BoxF& box,
+                                base::debug::TracedValue* res) {
+  res->AppendInteger(box.x());
+  res->AppendInteger(box.y());
+  res->AppendInteger(box.z());
+  res->AppendInteger(box.width());
+  res->AppendInteger(box.height());
+  res->AppendInteger(box.depth());
+}
+
+double MathUtil::AsDoubleSafely(double value) {
+  return std::min(value, std::numeric_limits<double>::max());
+}
+
+float MathUtil::AsFloatSafely(float value) {
+  return std::min(value, std::numeric_limits<float>::max());
+}
+
+}  // namespace cc
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
new file mode 100644
index 0000000..622ea4d
--- /dev/null
+++ b/cc/base/math_util.h
@@ -0,0 +1,235 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_MATH_UTIL_H_
+#define CC_BASE_MATH_UTIL_H_
+
+#include <algorithm>
+#include <cmath>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/scroll_offset.h"
+#include "ui/gfx/point3_f.h"
+#include "ui/gfx/point_f.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+
+namespace base {
+class Value;
+namespace debug {
+class TracedValue;
+}
+}
+
+namespace gfx {
+class QuadF;
+class Rect;
+class RectF;
+class Transform;
+class Vector2dF;
+class Vector2d;
+}
+
+namespace cc {
+
+struct HomogeneousCoordinate {
+  HomogeneousCoordinate(SkMScalar x, SkMScalar y, SkMScalar z, SkMScalar w) {
+    vec[0] = x;
+    vec[1] = y;
+    vec[2] = z;
+    vec[3] = w;
+  }
+
+  bool ShouldBeClipped() const { return w() <= 0.0; }
+
+  gfx::PointF CartesianPoint2d() const {
+    if (w() == SK_MScalar1)
+      return gfx::PointF(x(), y());
+
+    // For now, because this code is used privately only by MathUtil, it should
+    // never be called when w == 0, and we do not yet need to handle that case.
+    DCHECK(w());
+    SkMScalar inv_w = SK_MScalar1 / w();
+    return gfx::PointF(x() * inv_w, y() * inv_w);
+  }
+
+  gfx::Point3F CartesianPoint3d() const {
+    if (w() == SK_MScalar1)
+      return gfx::Point3F(x(), y(), z());
+
+    // For now, because this code is used privately only by MathUtil, it should
+    // never be called when w == 0, and we do not yet need to handle that case.
+    DCHECK(w());
+    SkMScalar inv_w = SK_MScalar1 / w();
+    return gfx::Point3F(x() * inv_w, y() * inv_w, z() * inv_w);
+  }
+
+  SkMScalar x() const { return vec[0]; }
+  SkMScalar y() const { return vec[1]; }
+  SkMScalar z() const { return vec[2]; }
+  SkMScalar w() const { return vec[3]; }
+
+  SkMScalar vec[4];
+};
+
+class CC_EXPORT MathUtil {
+ public:
+  static const double kPiDouble;
+  static const float kPiFloat;
+
+  static double Deg2Rad(double deg) { return deg * kPiDouble / 180.0; }
+  static double Rad2Deg(double rad) { return rad * 180.0 / kPiDouble; }
+
+  static float Deg2Rad(float deg) { return deg * kPiFloat / 180.0f; }
+  static float Rad2Deg(float rad) { return rad * 180.0f / kPiFloat; }
+
+  static float Round(float f) {
+    return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
+  }
+  static double Round(double d) {
+    return (d > 0.0) ? std::floor(d + 0.5) : std::ceil(d - 0.5);
+  }
+
+  template <typename T> static T ClampToRange(T value, T min, T max) {
+    return std::min(std::max(value, min), max);
+  }
+
+  // Background: Existing transform code does not do the right thing in
+  // MapRect / MapQuad / ProjectQuad when there is a perspective projection that
+  // causes one of the transformed vertices to go to w < 0. In those cases, it
+  // is necessary to perform clipping in homogeneous coordinates, after applying
+  // the transform, before dividing-by-w to convert to cartesian coordinates.
+  //
+  // These functions return the axis-aligned rect that encloses the correctly
+  // clipped, transformed polygon.
+  static gfx::Rect MapEnclosingClippedRect(const gfx::Transform& transform,
+                                           const gfx::Rect& rect);
+  static gfx::RectF MapClippedRect(const gfx::Transform& transform,
+                                   const gfx::RectF& rect);
+  static gfx::Rect ProjectEnclosingClippedRect(const gfx::Transform& transform,
+                                               const gfx::Rect& rect);
+  static gfx::RectF ProjectClippedRect(const gfx::Transform& transform,
+                                       const gfx::RectF& rect);
+
+  // This function is only valid when the transform preserves 2d axis
+  // alignment and the resulting rect will not be clipped.
+  static gfx::Rect MapEnclosedRectWith2dAxisAlignedTransform(
+      const gfx::Transform& transform,
+      const gfx::Rect& rect);
+
+  // Returns an array of vertices that represent the clipped polygon. After
+  // returning, indexes from 0 to num_vertices_in_clipped_quad are valid in the
+  // clipped_quad array. Note that num_vertices_in_clipped_quad may be zero,
+  // which means the entire quad was clipped, and none of the vertices in the
+  // array are valid.
+  static void MapClippedQuad(const gfx::Transform& transform,
+                             const gfx::QuadF& src_quad,
+                             gfx::PointF clipped_quad[8],
+                             int* num_vertices_in_clipped_quad);
+  static bool MapClippedQuad3d(const gfx::Transform& transform,
+                               const gfx::QuadF& src_quad,
+                               gfx::Point3F clipped_quad[8],
+                               int* num_vertices_in_clipped_quad);
+
+  static gfx::RectF ComputeEnclosingRectOfVertices(const gfx::PointF vertices[],
+                                                   int num_vertices);
+  static gfx::RectF ComputeEnclosingClippedRect(
+      const HomogeneousCoordinate& h1,
+      const HomogeneousCoordinate& h2,
+      const HomogeneousCoordinate& h3,
+      const HomogeneousCoordinate& h4);
+
+  // NOTE: These functions do not do correct clipping against w = 0 plane, but
+  // they correctly detect the clipped condition via the boolean clipped.
+  static gfx::QuadF MapQuad(const gfx::Transform& transform,
+                            const gfx::QuadF& quad,
+                            bool* clipped);
+  static gfx::QuadF MapQuad3d(const gfx::Transform& transform,
+                              const gfx::QuadF& q,
+                              gfx::Point3F* p,
+                              bool* clipped);
+  static gfx::PointF MapPoint(const gfx::Transform& transform,
+                              const gfx::PointF& point,
+                              bool* clipped);
+  static gfx::Point3F MapPoint(const gfx::Transform&,
+                               const gfx::Point3F&,
+                               bool* clipped);
+  static gfx::QuadF ProjectQuad(const gfx::Transform& transform,
+                                const gfx::QuadF& quad,
+                                bool* clipped);
+  static gfx::PointF ProjectPoint(const gfx::Transform& transform,
+                                  const gfx::PointF& point,
+                                  bool* clipped);
+  // Identical to the above function, but coerces the homogeneous coordinate to
+  // a 3d rather than a 2d point.
+  static gfx::Point3F ProjectPoint3D(const gfx::Transform& transform,
+                                     const gfx::PointF& point,
+                                     bool* clipped);
+
+  static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&,
+                                                          float fallbackValue);
+
+  // Makes a rect that has the same relationship to input_outer_rect as
+  // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be
+  // contained within scale_outer_rect, and likewise the rectangle that is
+  // returned will be within input_outer_rect at a similar relative, scaled
+  // position.
+  static gfx::RectF ScaleRectProportional(const gfx::RectF& input_outer_rect,
+                                          const gfx::RectF& scale_outer_rect,
+                                          const gfx::RectF& scale_inner_rect);
+
+  // Returns the smallest angle between the given two vectors in degrees.
+  // Neither vector is assumed to be normalized.
+  static float SmallestAngleBetweenVectors(const gfx::Vector2dF& v1,
+                                           const gfx::Vector2dF& v2);
+
+  // Projects the |source| vector onto |destination|. Neither vector is assumed
+  // to be normalized.
+  static gfx::Vector2dF ProjectVector(const gfx::Vector2dF& source,
+                                      const gfx::Vector2dF& destination);
+
+  // Conversion to value.
+  static scoped_ptr<base::Value> AsValue(const gfx::Size& s);
+  static scoped_ptr<base::Value> AsValue(const gfx::Rect& r);
+  static bool FromValue(const base::Value*, gfx::Rect* out_rect);
+  static scoped_ptr<base::Value> AsValue(const gfx::PointF& q);
+
+  static void AddToTracedValue(const gfx::Size& s,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::SizeF& s,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::Rect& r,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::PointF& q,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::Point3F&,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::Vector2d& v,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::Vector2dF& v,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::ScrollOffset& v,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::QuadF& q,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::RectF& rect,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::Transform& transform,
+                               base::debug::TracedValue* res);
+  static void AddToTracedValue(const gfx::BoxF& box,
+                               base::debug::TracedValue* res);
+
+  // Returns a base::Value representation of the floating point value.
+  // If the value is inf, returns max double/float representation.
+  static double AsDoubleSafely(double value);
+  static float AsFloatSafely(float value);
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_MATH_UTIL_H_
diff --git a/cc/base/math_util_unittest.cc b/cc/base/math_util_unittest.cc
new file mode 100644
index 0000000..fb8c27f
--- /dev/null
+++ b/cc/base/math_util_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/math_util.h"
+
+#include <cmath>
+
+#include "cc/test/geometry_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+TEST(MathUtilTest, ProjectionOfPerpendicularPlane) {
+  // In this case, the m33() element of the transform becomes zero, which could
+  // cause a divide-by-zero when projecting points/quads.
+
+  gfx::Transform transform;
+  transform.MakeIdentity();
+  transform.matrix().set(2, 2, 0);
+
+  gfx::RectF rect = gfx::RectF(0, 0, 1, 1);
+  gfx::RectF projected_rect = MathUtil::ProjectClippedRect(transform, rect);
+
+  EXPECT_EQ(0, projected_rect.x());
+  EXPECT_EQ(0, projected_rect.y());
+  EXPECT_TRUE(projected_rect.IsEmpty());
+}
+
+TEST(MathUtilTest, EnclosingClippedRectUsesCorrectInitialBounds) {
+  HomogeneousCoordinate h1(-100, -100, 0, 1);
+  HomogeneousCoordinate h2(-10, -10, 0, 1);
+  HomogeneousCoordinate h3(10, 10, 0, -1);
+  HomogeneousCoordinate h4(100, 100, 0, -1);
+
+  // The bounds of the enclosing clipped rect should be -100 to -10 for both x
+  // and y. However, if there is a bug where the initial xmin/xmax/ymin/ymax are
+  // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
+  // then the enclosing clipped rect will be computed incorrectly.
+  gfx::RectF result = MathUtil::ComputeEnclosingClippedRect(h1, h2, h3, h4);
+
+  // Due to floating point math in ComputeClippedPointForEdge this result
+  // is fairly imprecise.  0.15f was empirically determined.
+  EXPECT_RECT_NEAR(
+      gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)), result, 0.15f);
+}
+
+TEST(MathUtilTest, EnclosingRectOfVerticesUsesCorrectInitialBounds) {
+  gfx::PointF vertices[3];
+  int num_vertices = 3;
+
+  vertices[0] = gfx::PointF(-10, -100);
+  vertices[1] = gfx::PointF(-100, -10);
+  vertices[2] = gfx::PointF(-30, -30);
+
+  // The bounds of the enclosing rect should be -100 to -10 for both x and y.
+  // However, if there is a bug where the initial xmin/xmax/ymin/ymax are
+  // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
+  // then the enclosing clipped rect will be computed incorrectly.
+  gfx::RectF result =
+      MathUtil::ComputeEnclosingRectOfVertices(vertices, num_vertices);
+
+  EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)),
+                       result);
+}
+
+TEST(MathUtilTest, SmallestAngleBetweenVectors) {
+  gfx::Vector2dF x(1, 0);
+  gfx::Vector2dF y(0, 1);
+  gfx::Vector2dF test_vector(0.5, 0.5);
+
+  // Orthogonal vectors are at an angle of 90 degress.
+  EXPECT_EQ(90, MathUtil::SmallestAngleBetweenVectors(x, y));
+
+  // A vector makes a zero angle with itself.
+  EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(x, x));
+  EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(y, y));
+  EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(test_vector, test_vector));
+
+  // Parallel but reversed vectors are at 180 degrees.
+  EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(x, -x));
+  EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(y, -y));
+  EXPECT_FLOAT_EQ(
+      180, MathUtil::SmallestAngleBetweenVectors(test_vector, -test_vector));
+
+  // The test vector is at a known angle.
+  EXPECT_FLOAT_EQ(
+      45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector, x)));
+  EXPECT_FLOAT_EQ(
+      45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector, y)));
+}
+
+TEST(MathUtilTest, VectorProjection) {
+  gfx::Vector2dF x(1, 0);
+  gfx::Vector2dF y(0, 1);
+  gfx::Vector2dF test_vector(0.3f, 0.7f);
+
+  // Orthogonal vectors project to a zero vector.
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(x, y));
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(y, x));
+
+  // Projecting a vector onto the orthonormal basis gives the corresponding
+  // component of the vector.
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(test_vector.x(), 0),
+                   MathUtil::ProjectVector(test_vector, x));
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, test_vector.y()),
+                   MathUtil::ProjectVector(test_vector, y));
+
+  // Finally check than an arbitrary vector projected to another one gives a
+  // vector parallel to the second vector.
+  gfx::Vector2dF target_vector(0.5, 0.2f);
+  gfx::Vector2dF projected_vector =
+      MathUtil::ProjectVector(test_vector, target_vector);
+  EXPECT_EQ(projected_vector.x() / target_vector.x(),
+            projected_vector.y() / target_vector.y());
+}
+
+TEST(MathUtilTest, MapEnclosedRectWith2dAxisAlignedTransform) {
+  gfx::Rect input(1, 2, 3, 4);
+  gfx::Rect output;
+  gfx::Transform transform;
+
+  // Identity.
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(input, output);
+
+  // Integer translate.
+  transform.Translate(2.0, 3.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(3, 5, 3, 4), output);
+
+  // Non-integer translate.
+  transform.Translate(0.5, 0.5);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(4, 6, 2, 3), output);
+
+  // Scale.
+  transform = gfx::Transform();
+  transform.Scale(2.0, 3.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(2, 6, 6, 12), output);
+
+  // Rotate Z.
+  transform = gfx::Transform();
+  transform.Translate(1.0, 2.0);
+  transform.RotateAboutZAxis(90.0);
+  transform.Translate(-1.0, -2.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(-3, 2, 4, 3), output);
+
+  // Rotate X.
+  transform = gfx::Transform();
+  transform.RotateAboutXAxis(90.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_TRUE(output.IsEmpty());
+
+  transform = gfx::Transform();
+  transform.RotateAboutXAxis(180.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(1, -6, 3, 4), output);
+
+  // Rotate Y.
+  transform = gfx::Transform();
+  transform.RotateAboutYAxis(90.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_TRUE(output.IsEmpty());
+
+  transform = gfx::Transform();
+  transform.RotateAboutYAxis(180.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(-4, 2, 3, 4), output);
+
+  // Translate Z.
+  transform = gfx::Transform();
+  transform.ApplyPerspectiveDepth(10.0);
+  transform.Translate3d(0.0, 0.0, 5.0);
+  output =
+      MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+  EXPECT_EQ(gfx::Rect(2, 4, 6, 8), output);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/ref_counted_managed.h b/cc/base/ref_counted_managed.h
new file mode 100644
index 0000000..8bb836f
--- /dev/null
+++ b/cc/base/ref_counted_managed.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_REF_COUNTED_MANAGED_H_
+#define CC_BASE_REF_COUNTED_MANAGED_H_
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+template <typename T> class RefCountedManaged;
+
+template <typename T>
+class CC_EXPORT RefCountedManager {
+ protected:
+  RefCountedManager() : live_object_count_(0) {}
+  ~RefCountedManager() {
+    CHECK_EQ(0, live_object_count_);
+  }
+
+  virtual void Release(T* object) = 0;
+
+ private:
+  friend class RefCountedManaged<T>;
+  int live_object_count_;
+};
+
+template <typename T>
+class CC_EXPORT RefCountedManaged : public base::subtle::RefCountedBase {
+ public:
+  explicit RefCountedManaged(RefCountedManager<T>* manager)
+      : manager_(manager) {
+    manager_->live_object_count_++;
+  }
+
+  void AddRef() const {
+    base::subtle::RefCountedBase::AddRef();
+  }
+
+  void Release() {
+    if (base::subtle::RefCountedBase::Release()) {
+      DCHECK_GT(manager_->live_object_count_, 0);
+      manager_->live_object_count_--;
+
+      // This must be the last statement in case manager deletes
+      // the object immediately.
+      manager_->Release(static_cast<T*>(this));
+    }
+  }
+
+ protected:
+  ~RefCountedManaged() {}
+
+ private:
+  RefCountedManager<T>* manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(RefCountedManaged<T>);
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_REF_COUNTED_MANAGED_H_
diff --git a/cc/base/region.cc b/cc/base/region.cc
new file mode 100644
index 0000000..d752b20
--- /dev/null
+++ b/cc/base/region.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/region.h"
+
+#include "base/debug/trace_event_argument.h"
+#include "base/values.h"
+#include "cc/base/simple_enclosed_region.h"
+
+namespace cc {
+
+Region::Region() {
+}
+
+Region::Region(const Region& region)
+    : skregion_(region.skregion_) {
+}
+
+Region::Region(const gfx::Rect& rect)
+    : skregion_(gfx::RectToSkIRect(rect)) {
+}
+
+Region::~Region() {
+}
+
+const Region& Region::operator=(const gfx::Rect& rect) {
+  skregion_ = SkRegion(gfx::RectToSkIRect(rect));
+  return *this;
+}
+
+const Region& Region::operator=(const Region& region) {
+  skregion_ = region.skregion_;
+  return *this;
+}
+
+void Region::Swap(Region* region) {
+  region->skregion_.swap(skregion_);
+}
+
+void Region::Clear() {
+  skregion_.setEmpty();
+}
+
+bool Region::IsEmpty() const {
+  return skregion_.isEmpty();
+}
+
+int Region::GetRegionComplexity() const {
+  return skregion_.computeRegionComplexity();
+}
+
+bool Region::Contains(const gfx::Point& point) const {
+  return skregion_.contains(point.x(), point.y());
+}
+
+bool Region::Contains(const gfx::Rect& rect) const {
+  if (rect.IsEmpty())
+    return true;
+  return skregion_.contains(gfx::RectToSkIRect(rect));
+}
+
+bool Region::Contains(const Region& region) const {
+  if (region.IsEmpty())
+    return true;
+  return skregion_.contains(region.skregion_);
+}
+
+bool Region::Intersects(const gfx::Rect& rect) const {
+  return skregion_.intersects(gfx::RectToSkIRect(rect));
+}
+
+bool Region::Intersects(const Region& region) const {
+  return skregion_.intersects(region.skregion_);
+}
+
+void Region::Subtract(const gfx::Rect& rect) {
+  skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kDifference_Op);
+}
+
+void Region::Subtract(const Region& region) {
+  skregion_.op(region.skregion_, SkRegion::kDifference_Op);
+}
+
+void Region::Subtract(const SimpleEnclosedRegion& region) {
+  for (size_t i = 0; i < region.GetRegionComplexity(); ++i) {
+    skregion_.op(gfx::RectToSkIRect(region.GetRect(i)),
+                 SkRegion::kDifference_Op);
+  }
+}
+
+void Region::Union(const gfx::Rect& rect) {
+  skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kUnion_Op);
+}
+
+void Region::Union(const Region& region) {
+  skregion_.op(region.skregion_, SkRegion::kUnion_Op);
+}
+
+void Region::Intersect(const gfx::Rect& rect) {
+  skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kIntersect_Op);
+}
+
+void Region::Intersect(const Region& region) {
+  skregion_.op(region.skregion_, SkRegion::kIntersect_Op);
+}
+
+std::string Region::ToString() const {
+  if (IsEmpty())
+    return gfx::Rect().ToString();
+
+  std::string result;
+  for (Iterator it(*this); it.has_rect(); it.next()) {
+    if (!result.empty())
+      result += " | ";
+    result += it.rect().ToString();
+  }
+  return result;
+}
+
+scoped_ptr<base::Value> Region::AsValue() const {
+  scoped_ptr<base::ListValue> result(new base::ListValue());
+  for (Iterator it(*this); it.has_rect(); it.next()) {
+    gfx::Rect rect(it.rect());
+    result->AppendInteger(rect.x());
+    result->AppendInteger(rect.y());
+    result->AppendInteger(rect.width());
+    result->AppendInteger(rect.height());
+  }
+  return result.Pass();
+}
+
+void Region::AsValueInto(base::debug::TracedValue* result) const {
+  for (Iterator it(*this); it.has_rect(); it.next()) {
+    gfx::Rect rect(it.rect());
+    result->AppendInteger(rect.x());
+    result->AppendInteger(rect.y());
+    result->AppendInteger(rect.width());
+    result->AppendInteger(rect.height());
+  }
+}
+
+Region::Iterator::Iterator() {
+}
+
+Region::Iterator::Iterator(const Region& region)
+    : it_(region.skregion_) {
+}
+
+Region::Iterator::~Iterator() {
+}
+
+}  // namespace cc
diff --git a/cc/base/region.h b/cc/base/region.h
new file mode 100644
index 0000000..d78d4cb
--- /dev/null
+++ b/cc/base/region.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_REGION_H_
+#define CC_BASE_REGION_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace base {
+class Value;
+namespace debug {
+class TracedValue;
+}
+}
+
+namespace cc {
+class SimpleEnclosedRegion;
+
+class CC_EXPORT Region {
+ public:
+  Region();
+  Region(const Region& region);
+  Region(const gfx::Rect& rect);  // NOLINT(runtime/explicit)
+  ~Region();
+
+  const Region& operator=(const gfx::Rect& rect);
+  const Region& operator=(const Region& region);
+
+  void Swap(Region* region);
+  void Clear();
+  bool IsEmpty() const;
+  int GetRegionComplexity() const;
+
+  bool Contains(const gfx::Point& point) const;
+  bool Contains(const gfx::Rect& rect) const;
+  bool Contains(const Region& region) const;
+
+  bool Intersects(const gfx::Rect& rect) const;
+  bool Intersects(const Region& region) const;
+
+  void Subtract(const gfx::Rect& rect);
+  void Subtract(const Region& region);
+  void Subtract(const SimpleEnclosedRegion& region);
+  void Union(const gfx::Rect& rect);
+  void Union(const Region& region);
+  void Intersect(const gfx::Rect& rect);
+  void Intersect(const Region& region);
+
+  bool Equals(const Region& other) const {
+    return skregion_ == other.skregion_;
+  }
+
+  gfx::Rect bounds() const {
+    return gfx::SkIRectToRect(skregion_.getBounds());
+  }
+
+  std::string ToString() const;
+  scoped_ptr<base::Value> AsValue() const;
+  void AsValueInto(base::debug::TracedValue* array) const;
+
+  class CC_EXPORT Iterator {
+   public:
+    Iterator();
+    explicit Iterator(const Region& region);
+    ~Iterator();
+
+    gfx::Rect rect() const {
+      return gfx::SkIRectToRect(it_.rect());
+    }
+
+    void next() {
+      it_.next();
+    }
+
+    bool has_rect() const {
+      return !it_.done();
+    }
+
+   private:
+    SkRegion::Iterator it_;
+  };
+
+ private:
+  SkRegion skregion_;
+};
+
+inline bool operator==(const Region& a, const Region& b) {
+  return a.Equals(b);
+}
+
+inline bool operator!=(const Region& a, const Region& b) {
+  return !(a == b);
+}
+
+inline Region SubtractRegions(const Region& a, const Region& b) {
+  Region result = a;
+  result.Subtract(b);
+  return result;
+}
+
+inline Region SubtractRegions(const Region& a, const gfx::Rect& b) {
+  Region result = a;
+  result.Subtract(b);
+  return result;
+}
+
+inline Region IntersectRegions(const Region& a, const Region& b) {
+  Region result = a;
+  result.Intersect(b);
+  return result;
+}
+
+inline Region IntersectRegions(const Region& a, const gfx::Rect& b) {
+  Region result = a;
+  result.Intersect(b);
+  return result;
+}
+
+inline Region UnionRegions(const Region& a, const Region& b) {
+  Region result = a;
+  result.Union(b);
+  return result;
+}
+
+inline Region UnionRegions(const Region& a, const gfx::Rect& b) {
+  Region result = a;
+  result.Union(b);
+  return result;
+}
+
+}  // namespace cc
+
+#endif  // CC_BASE_REGION_H_
diff --git a/cc/base/region_unittest.cc b/cc/base/region_unittest.cc
new file mode 100644
index 0000000..c9a218d
--- /dev/null
+++ b/cc/base/region_unittest.cc
@@ -0,0 +1,454 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/region.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+#define TEST_INSIDE_RECT(r, x, y, w, h)                      \
+  EXPECT_TRUE(r.Contains(gfx::Point(x, y)));                 \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y)));         \
+  EXPECT_TRUE(r.Contains(gfx::Point(x, y + h - 1)));         \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y + h - 1))); \
+  EXPECT_TRUE(r.Contains(gfx::Point(x, y + h / 2)));         \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y + h / 2))); \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y)));         \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y + h - 1))); \
+  EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y + h / 2))); \
+
+#define TEST_LEFT_OF_RECT(r, x, y, w, h)                  \
+  EXPECT_FALSE(r.Contains(gfx::Point(x - 1, y)));         \
+  EXPECT_FALSE(r.Contains(gfx::Point(x - 1, y + h - 1))); \
+
+#define TEST_RIGHT_OF_RECT(r, x, y, w, h)                 \
+  EXPECT_FALSE(r.Contains(gfx::Point(x + w, y)));         \
+  EXPECT_FALSE(r.Contains(gfx::Point(x + w, y + h - 1))); \
+
+#define TEST_TOP_OF_RECT(r, x, y, w, h)                   \
+  EXPECT_FALSE(r.Contains(gfx::Point(x, y - 1)));         \
+  EXPECT_FALSE(r.Contains(gfx::Point(x + w - 1, y - 1))); \
+
+#define TEST_BOTTOM_OF_RECT(r, x, y, w, h)                \
+  EXPECT_FALSE(r.Contains(gfx::Point(x, y + h)));         \
+  EXPECT_FALSE(r.Contains(gfx::Point(x + w - 1, y + h))); \
+
+TEST(RegionTest, ContainsPoint) {
+  Region r;
+
+  EXPECT_FALSE(r.Contains(gfx::Point(0, 0)));
+
+  r.Union(gfx::Rect(35, 35, 1, 1));
+  TEST_INSIDE_RECT(r, 35, 35, 1, 1);
+  TEST_LEFT_OF_RECT(r, 35, 35, 1, 1);
+  TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1);
+  TEST_TOP_OF_RECT(r, 35, 35, 1, 1);
+  TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1);
+
+  r.Union(gfx::Rect(30, 30, 10, 10));
+  TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+  TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+  TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10);
+
+  r.Union(gfx::Rect(31, 40, 10, 10));
+  EXPECT_FALSE(r.Contains(gfx::Point(30, 40)));
+  EXPECT_TRUE(r.Contains(gfx::Point(31, 40)));
+  EXPECT_FALSE(r.Contains(gfx::Point(40, 39)));
+  EXPECT_TRUE(r.Contains(gfx::Point(40, 40)));
+
+  TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+  TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+  TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+  TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+  TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+
+  r.Union(gfx::Rect(42, 40, 10, 10));
+
+  TEST_INSIDE_RECT(r, 42, 40, 10, 10);
+  TEST_LEFT_OF_RECT(r, 42, 40, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10);
+  TEST_TOP_OF_RECT(r, 42, 40, 10, 10);
+  TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10);
+
+  TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+  TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+  TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+  TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+  TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+  TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+  TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+}
+
+TEST(RegionTest, EmptySpan) {
+  Region r;
+  r.Union(gfx::Rect(5, 0, 10, 10));
+  r.Union(gfx::Rect(0, 5, 10, 10));
+  r.Subtract(gfx::Rect(7, 7, 10, 0));
+
+  for (Region::Iterator it(r); it.has_rect(); it.next())
+    EXPECT_FALSE(it.rect().IsEmpty());
+}
+
+#define TEST_NO_INTERSECT(a, b) {  \
+  Region ar = a;                   \
+  Region br = b;                   \
+  EXPECT_FALSE(ar.Intersects(br)); \
+  EXPECT_FALSE(br.Intersects(ar)); \
+  EXPECT_FALSE(ar.Intersects(b));  \
+  EXPECT_FALSE(br.Intersects(a));  \
+}
+
+#define TEST_INTERSECT(a, b) {    \
+  Region ar = a;                  \
+  Region br = b;                  \
+  EXPECT_TRUE(ar.Intersects(br)); \
+  EXPECT_TRUE(br.Intersects(ar)); \
+  EXPECT_TRUE(ar.Intersects(b));  \
+  EXPECT_TRUE(br.Intersects(a));  \
+}
+
+TEST(RegionTest, IntersectsRegion) {
+  Region r;
+
+  TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect());
+  TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect(0, 0, 1, 1));
+  TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect(1, 1, 1, 1));
+
+  TEST_NO_INTERSECT(gfx::Rect(-1, -1, 2, 2), gfx::Rect());
+
+  r.Union(gfx::Rect(0, 0, 1, 1));
+  TEST_NO_INTERSECT(r, gfx::Rect());
+  TEST_INTERSECT(r, gfx::Rect(0, 0, 1, 1));
+  TEST_INTERSECT(r, gfx::Rect(0, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(-1, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(-1, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(0, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(-1, -1, 3, 3));
+
+  r.Union(gfx::Rect(0, 0, 3, 3));
+  r.Union(gfx::Rect(10, 0, 3, 3));
+  r.Union(gfx::Rect(0, 10, 13, 3));
+  TEST_NO_INTERSECT(r, gfx::Rect());
+  TEST_INTERSECT(r, gfx::Rect(1, 1, 1, 1));
+  TEST_INTERSECT(r, gfx::Rect(0, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(1, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(1, 1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(0, 1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(0, 0, 3, 3));
+  TEST_INTERSECT(r, gfx::Rect(-1, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(2, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(2, 2, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(-1, 2, 2, 2));
+
+  TEST_INTERSECT(r, gfx::Rect(11, 1, 1, 1));
+  TEST_INTERSECT(r, gfx::Rect(10, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(11, 0, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(11, 1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(10, 1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(10, 0, 3, 3));
+  TEST_INTERSECT(r, gfx::Rect(9, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(12, -1, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(12, 2, 2, 2));
+  TEST_INTERSECT(r, gfx::Rect(9, 2, 2, 2));
+
+  TEST_INTERSECT(r, gfx::Rect(0, -1, 13, 5));
+  TEST_INTERSECT(r, gfx::Rect(1, -1, 11, 5));
+  TEST_INTERSECT(r, gfx::Rect(2, -1, 9, 5));
+  TEST_INTERSECT(r, gfx::Rect(2, -1, 8, 5));
+  TEST_INTERSECT(r, gfx::Rect(3, -1, 8, 5));
+  TEST_NO_INTERSECT(r, gfx::Rect(3, -1, 7, 5));
+
+  TEST_INTERSECT(r, gfx::Rect(0, 1, 13, 1));
+  TEST_INTERSECT(r, gfx::Rect(1, 1, 11, 1));
+  TEST_INTERSECT(r, gfx::Rect(2, 1, 9, 1));
+  TEST_INTERSECT(r, gfx::Rect(2, 1, 8, 1));
+  TEST_INTERSECT(r, gfx::Rect(3, 1, 8, 1));
+  TEST_NO_INTERSECT(r, gfx::Rect(3, 1, 7, 1));
+
+  TEST_INTERSECT(r, gfx::Rect(0, 0, 13, 13));
+  TEST_INTERSECT(r, gfx::Rect(0, 1, 13, 11));
+  TEST_INTERSECT(r, gfx::Rect(0, 2, 13, 9));
+  TEST_INTERSECT(r, gfx::Rect(0, 2, 13, 8));
+  TEST_INTERSECT(r, gfx::Rect(0, 3, 13, 8));
+  TEST_NO_INTERSECT(r, gfx::Rect(0, 3, 13, 7));
+}
+
+TEST(RegionTest, ReadPastFullSpanVectorInIntersectsTest) {
+  Region r;
+
+  // This region has enough spans to fill its allocated Vector exactly.
+  r.Union(gfx::Rect(400, 300, 1, 800));
+  r.Union(gfx::Rect(785, 585, 1, 1));
+  r.Union(gfx::Rect(787, 585, 1, 1));
+  r.Union(gfx::Rect(0, 587, 16, 162));
+  r.Union(gfx::Rect(26, 590, 300, 150));
+  r.Union(gfx::Rect(196, 750, 1, 1));
+  r.Union(gfx::Rect(0, 766, 1, 1));
+  r.Union(gfx::Rect(0, 782, 1, 1));
+  r.Union(gfx::Rect(745, 798, 1, 1));
+  r.Union(gfx::Rect(795, 882, 10, 585));
+  r.Union(gfx::Rect(100, 1499, 586, 1));
+  r.Union(gfx::Rect(100, 1500, 585, 784));
+  // This query rect goes past the bottom of the Region, causing the
+  // test to reach the last span and try go past it. It should not read
+  // memory off the end of the span Vector.
+  TEST_NO_INTERSECT(r, gfx::Rect(0, 2184, 1, 150));
+}
+
+#define TEST_NO_CONTAINS(a, b)                  \
+  {                                             \
+    Region ar = a;                              \
+    Region br = b;                              \
+    EXPECT_FALSE(ar.Contains(br));              \
+    EXPECT_FALSE(ar.Contains(b));               \
+  }
+
+#define TEST_CONTAINS(a, b)                     \
+  {                                             \
+    Region ar = a;                              \
+    Region br = b;                              \
+    EXPECT_TRUE(ar.Contains(br));               \
+    EXPECT_TRUE(ar.Contains(b));                \
+  }
+
+TEST(RegionTest, ContainsRegion) {
+  TEST_CONTAINS(gfx::Rect(), gfx::Rect());
+  TEST_CONTAINS(gfx::Rect(0, 0, 1, 1), gfx::Rect());
+  TEST_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect());
+
+  TEST_NO_CONTAINS(gfx::Rect(), gfx::Rect(0, 0, 1, 1));
+  TEST_NO_CONTAINS(gfx::Rect(), gfx::Rect(1, 1, 1, 1));
+
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(11, 10, 1, 1));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 11, 1, 1));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 10, 1, 1));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 9, 1, 1));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 9, 2, 2));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 9, 2, 2));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 10, 2, 2));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 10, 2, 2));
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 9, 3, 3));
+
+  Region h_lines;
+  for (int i = 10; i < 20; i += 2)
+    h_lines.Union(gfx::Rect(i, 10, 1, 10));
+
+  TEST_CONTAINS(gfx::Rect(10, 10, 9, 10), h_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 9), h_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 11, 9, 9), h_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 8, 10), h_lines);
+  TEST_NO_CONTAINS(gfx::Rect(11, 10, 8, 10), h_lines);
+
+  Region v_lines;
+  for (int i = 10; i < 20; i += 2)
+    v_lines.Union(gfx::Rect(10, i, 10, 1));
+
+  TEST_CONTAINS(gfx::Rect(10, 10, 10, 9), v_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 9), v_lines);
+  TEST_NO_CONTAINS(gfx::Rect(11, 10, 9, 9), v_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 10, 8), v_lines);
+  TEST_NO_CONTAINS(gfx::Rect(10, 11, 10, 8), v_lines);
+
+  Region grid;
+  for (int i = 10; i < 20; i += 2)
+    for (int j = 10; j < 20; j += 2)
+      grid.Union(gfx::Rect(i, j, 1, 1));
+
+  TEST_CONTAINS(gfx::Rect(10, 10, 9, 9), grid);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 8), grid);
+  TEST_NO_CONTAINS(gfx::Rect(10, 11, 9, 8), grid);
+  TEST_NO_CONTAINS(gfx::Rect(10, 10, 8, 9), grid);
+  TEST_NO_CONTAINS(gfx::Rect(11, 10, 8, 9), grid);
+
+  TEST_CONTAINS(h_lines, h_lines);
+  TEST_CONTAINS(v_lines, v_lines);
+  TEST_NO_CONTAINS(v_lines, h_lines);
+  TEST_NO_CONTAINS(h_lines, v_lines);
+  TEST_CONTAINS(grid, grid);
+  TEST_CONTAINS(h_lines, grid);
+  TEST_CONTAINS(v_lines, grid);
+  TEST_NO_CONTAINS(grid, h_lines);
+  TEST_NO_CONTAINS(grid, v_lines);
+
+  for (int i = 10; i < 20; i += 2)
+    TEST_CONTAINS(h_lines, gfx::Rect(i, 10, 1, 10));
+
+  for (int i = 10; i < 20; i += 2)
+    TEST_CONTAINS(v_lines, gfx::Rect(10, i, 10, 1));
+
+  for (int i = 10; i < 20; i += 2)
+    for (int j = 10; j < 20; j += 2)
+      TEST_CONTAINS(grid, gfx::Rect(i, j, 1, 1));
+
+  Region container;
+  container.Union(gfx::Rect(0, 0, 40, 20));
+  container.Union(gfx::Rect(0, 20, 41, 20));
+  TEST_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 10, 10));
+  container.Union(gfx::Rect(0, 30, 10, 10));
+  container.Union(gfx::Rect(30, 30, 10, 10));
+  container.Union(gfx::Rect(30, 0, 10, 10));
+  TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 10, 10));
+  container.Union(gfx::Rect(0, 30, 10, 10));
+  container.Union(gfx::Rect(30, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+  container.Clear();
+  container.Union(gfx::Rect(30, 0, 10, 10));
+  container.Union(gfx::Rect(30, 30, 10, 10));
+  container.Union(gfx::Rect(0, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 10, 40));
+  container.Union(gfx::Rect(30, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 40, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(10, -1, 20, 10));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 40, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(10, 31, 20, 10));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 40, 20));
+  container.Union(gfx::Rect(0, 20, 41, 20));
+  TEST_NO_CONTAINS(container, gfx::Rect(-1, 10, 10, 20));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 40, 20));
+  container.Union(gfx::Rect(0, 20, 41, 20));
+  TEST_NO_CONTAINS(container, gfx::Rect(31, 10, 10, 20));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 40, 40));
+  container.Subtract(gfx::Rect(0, 20, 60, 0));
+  TEST_NO_CONTAINS(container, gfx::Rect(31, 10, 10, 20));
+
+  container.Clear();
+  container.Union(gfx::Rect(0, 0, 60, 20));
+  container.Union(gfx::Rect(30, 20, 10, 20));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 39));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 41));
+  TEST_NO_CONTAINS(container, gfx::Rect(29, 0, 10, 39));
+  TEST_CONTAINS(container, gfx::Rect(30, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(31, 0, 10, 41));
+  TEST_NO_CONTAINS(container, gfx::Rect(49, 0, 10, 39));
+  TEST_NO_CONTAINS(container, gfx::Rect(50, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(51, 0, 10, 41));
+
+  container.Clear();
+  container.Union(gfx::Rect(30, 0, 10, 20));
+  container.Union(gfx::Rect(0, 20, 60, 20));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 39));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 41));
+  TEST_NO_CONTAINS(container, gfx::Rect(29, 0, 10, 39));
+  TEST_CONTAINS(container, gfx::Rect(30, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(31, 0, 10, 41));
+  TEST_NO_CONTAINS(container, gfx::Rect(49, 0, 10, 39));
+  TEST_NO_CONTAINS(container, gfx::Rect(50, 0, 10, 40));
+  TEST_NO_CONTAINS(container, gfx::Rect(51, 0, 10, 41));
+}
+
+TEST(RegionTest, Union) {
+  Region r;
+  Region r2;
+
+  // A rect uniting a contained rect does not change the region.
+  r2 = r = gfx::Rect(0, 0, 50, 50);
+  r2.Union(gfx::Rect(20, 20, 10, 10));
+  EXPECT_EQ(r, r2);
+
+  // A rect uniting a containing rect gives back the containing rect.
+  r = gfx::Rect(0, 0, 50, 50);
+  r.Union(gfx::Rect(0, 0, 100, 100));
+  EXPECT_EQ(Region(gfx::Rect(0, 0, 100, 100)), r);
+
+  // A complex region uniting a contained rect does not change the region.
+  r = gfx::Rect(0, 0, 50, 50);
+  r.Union(gfx::Rect(100, 0, 50, 50));
+  r2 = r;
+  r2.Union(gfx::Rect(20, 20, 10, 10));
+  EXPECT_EQ(r, r2);
+
+  // A complex region uniting a containing rect gives back the containing rect.
+  r = gfx::Rect(0, 0, 50, 50);
+  r.Union(gfx::Rect(100, 0, 50, 50));
+  r.Union(gfx::Rect(0, 0, 500, 500));
+  EXPECT_EQ(Region(gfx::Rect(0, 0, 500, 500)), r);
+}
+
+TEST(RegionTest, IsEmpty) {
+  EXPECT_TRUE(Region().IsEmpty());
+  EXPECT_TRUE(Region(gfx::Rect()).IsEmpty());
+  EXPECT_TRUE(Region(Region()).IsEmpty());
+  EXPECT_TRUE(Region(gfx::Rect(10, 10, 10, 0)).IsEmpty());
+  EXPECT_TRUE(Region(gfx::Rect(10, 10, 0, 10)).IsEmpty());
+  EXPECT_TRUE(Region(gfx::Rect(-10, 10, 10, 0)).IsEmpty());
+  EXPECT_TRUE(Region(gfx::Rect(-10, 10, 0, 10)).IsEmpty());
+  EXPECT_FALSE(Region(gfx::Rect(-1, -1, 1, 1)).IsEmpty());
+  EXPECT_FALSE(Region(gfx::Rect(0, 0, 1, 1)).IsEmpty());
+  EXPECT_FALSE(Region(gfx::Rect(0, 0, 2, 2)).IsEmpty());
+
+  EXPECT_TRUE(SkIRect::MakeXYWH(10, 10, 10, 0).isEmpty());
+  EXPECT_TRUE(SkIRect::MakeXYWH(10, 10, 0, 10).isEmpty());
+  EXPECT_TRUE(SkIRect::MakeXYWH(-10, 10, 10, 0).isEmpty());
+  EXPECT_TRUE(SkIRect::MakeXYWH(-10, 10, 0, 10).isEmpty());
+  EXPECT_FALSE(SkIRect::MakeXYWH(-1, -1, 1, 1).isEmpty());
+  EXPECT_FALSE(SkIRect::MakeXYWH(0, 0, 1, 1).isEmpty());
+  EXPECT_FALSE(SkIRect::MakeXYWH(0, 0, 2, 2).isEmpty());
+}
+
+TEST(RegionTest, Clear) {
+  Region r;
+
+  r = gfx::Rect(0, 0, 50, 50);
+  EXPECT_FALSE(r.IsEmpty());
+  r.Clear();
+  EXPECT_TRUE(r.IsEmpty());
+
+  r = gfx::Rect(0, 0, 50, 50);
+  r.Union(gfx::Rect(100, 0, 50, 50));
+  r.Union(gfx::Rect(0, 0, 500, 500));
+  EXPECT_FALSE(r.IsEmpty());
+  r.Clear();
+  EXPECT_TRUE(r.IsEmpty());
+}
+
+TEST(RegionSwap, Swap) {
+  Region r1, r2, r3;
+
+  r1 = gfx::Rect(0, 0, 50, 50);
+  r1.Swap(&r2);
+  EXPECT_TRUE(r1.IsEmpty());
+  EXPECT_EQ(r2.ToString(), Region(gfx::Rect(0, 0, 50, 50)).ToString());
+
+  r1 = gfx::Rect(0, 0, 50, 50);
+  r1.Union(gfx::Rect(100, 0, 50, 50));
+  r1.Union(gfx::Rect(0, 0, 500, 500));
+  r3 = r1;
+  r1.Swap(&r2);
+  EXPECT_EQ(r1.ToString(), Region(gfx::Rect(0, 0, 50, 50)).ToString());
+  EXPECT_EQ(r2.ToString(), r3.ToString());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/rolling_time_delta_history.cc b/cc/base/rolling_time_delta_history.cc
new file mode 100644
index 0000000..0f95cc5
--- /dev/null
+++ b/cc/base/rolling_time_delta_history.cc
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+
+#include "cc/base/rolling_time_delta_history.h"
+
+namespace cc {
+
+RollingTimeDeltaHistory::RollingTimeDeltaHistory(size_t max_size)
+    : max_size_(max_size) {}
+
+RollingTimeDeltaHistory::~RollingTimeDeltaHistory() {}
+
+void RollingTimeDeltaHistory::InsertSample(base::TimeDelta time) {
+  if (max_size_ == 0)
+    return;
+
+  if (sample_set_.size() == max_size_) {
+    sample_set_.erase(chronological_sample_deque_.front());
+    chronological_sample_deque_.pop_front();
+  }
+
+  TimeDeltaMultiset::iterator it = sample_set_.insert(time);
+  chronological_sample_deque_.push_back(it);
+}
+
+size_t RollingTimeDeltaHistory::SampleCount() {
+  return sample_set_.size();
+}
+
+void RollingTimeDeltaHistory::Clear() {
+  chronological_sample_deque_.clear();
+  sample_set_.clear();
+}
+
+base::TimeDelta RollingTimeDeltaHistory::Percentile(double percent) const {
+  if (sample_set_.size() == 0)
+    return base::TimeDelta();
+
+  double fraction = percent / 100.0;
+
+  if (fraction <= 0.0)
+    return *(sample_set_.begin());
+
+  if (fraction >= 1.0)
+    return *(sample_set_.rbegin());
+
+  size_t num_smaller_samples =
+      static_cast<size_t>(std::ceil(fraction * sample_set_.size())) - 1;
+
+  if (num_smaller_samples > sample_set_.size() / 2) {
+    size_t num_larger_samples = sample_set_.size() - num_smaller_samples - 1;
+    TimeDeltaMultiset::const_reverse_iterator it = sample_set_.rbegin();
+    for (size_t i = 0; i < num_larger_samples; i++)
+      it++;
+    return *it;
+  }
+
+  TimeDeltaMultiset::const_iterator it = sample_set_.begin();
+  for (size_t i = 0; i < num_smaller_samples; i++)
+    it++;
+  return *it;
+}
+
+}  // namespace cc
diff --git a/cc/base/rolling_time_delta_history.h b/cc/base/rolling_time_delta_history.h
new file mode 100644
index 0000000..603c813
--- /dev/null
+++ b/cc/base/rolling_time_delta_history.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_ROLLING_TIME_DELTA_HISTORY_H_
+#define CC_BASE_ROLLING_TIME_DELTA_HISTORY_H_
+
+#include <deque>
+#include <set>
+
+#include "base/time/time.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+// Stores a limited number of samples. When the maximum size is reached, each
+// insertion results in the deletion of the oldest remaining sample.
+class CC_EXPORT RollingTimeDeltaHistory {
+ public:
+  explicit RollingTimeDeltaHistory(size_t max_size);
+
+  ~RollingTimeDeltaHistory();
+
+  void InsertSample(base::TimeDelta time);
+
+  size_t SampleCount();
+
+  void Clear();
+
+  // Returns the smallest sample that is greater than or equal to the specified
+  // percent of samples. If there aren't any samples, returns base::TimeDelta().
+  base::TimeDelta Percentile(double percent) const;
+
+ private:
+  typedef std::multiset<base::TimeDelta> TimeDeltaMultiset;
+
+  TimeDeltaMultiset sample_set_;
+  std::deque<TimeDeltaMultiset::iterator> chronological_sample_deque_;
+  size_t max_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(RollingTimeDeltaHistory);
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_ROLLING_TIME_DELTA_HISTORY_H_
diff --git a/cc/base/rolling_time_delta_history_unittest.cc b/cc/base/rolling_time_delta_history_unittest.cc
new file mode 100644
index 0000000..5458240
--- /dev/null
+++ b/cc/base/rolling_time_delta_history_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/rolling_time_delta_history.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(RollingTimeDeltaHistoryTest, EmptyHistory) {
+  RollingTimeDeltaHistory empty_history(0);
+
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+
+  empty_history.InsertSample(base::TimeDelta::FromMilliseconds(10));
+  empty_history.InsertSample(base::TimeDelta::FromMilliseconds(15));
+  empty_history.InsertSample(base::TimeDelta::FromMilliseconds(20));
+
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+
+  empty_history.Clear();
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+}
+
+TEST(RollingTimeDeltaHistoryTest, SizeOneHistory) {
+  RollingTimeDeltaHistory size_one_history(1);
+  base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(10);
+  base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(20);
+
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
+
+  size_one_history.InsertSample(sample1);
+  EXPECT_EQ(sample1, size_one_history.Percentile(0.0));
+  EXPECT_EQ(sample1, size_one_history.Percentile(50.0));
+  EXPECT_EQ(sample1, size_one_history.Percentile(100.0));
+
+  size_one_history.InsertSample(sample2);
+  EXPECT_EQ(sample2, size_one_history.Percentile(0.0));
+  EXPECT_EQ(sample2, size_one_history.Percentile(50.0));
+  EXPECT_EQ(sample2, size_one_history.Percentile(100.0));
+
+  size_one_history.Clear();
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
+}
+
+TEST(RollingTimeDeltaHistoryTest, LargeHistory) {
+  RollingTimeDeltaHistory large_history(100);
+  base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(150);
+  base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(250);
+  base::TimeDelta sample3 = base::TimeDelta::FromMilliseconds(200);
+
+  large_history.InsertSample(sample1);
+  large_history.InsertSample(sample2);
+
+  EXPECT_EQ(sample1, large_history.Percentile(0.0));
+  EXPECT_EQ(sample1, large_history.Percentile(25.0));
+  EXPECT_EQ(sample2, large_history.Percentile(75.0));
+  EXPECT_EQ(sample2, large_history.Percentile(100.0));
+
+  large_history.InsertSample(sample3);
+  EXPECT_EQ(sample1, large_history.Percentile(0.0));
+  EXPECT_EQ(sample1, large_history.Percentile(25.0));
+  EXPECT_EQ(sample3, large_history.Percentile(50.0));
+  EXPECT_EQ(sample2, large_history.Percentile(100.0));
+
+  // Fill the history.
+  for (int i = 1; i <= 97; i++)
+    large_history.InsertSample(base::TimeDelta::FromMilliseconds(i));
+
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+            large_history.Percentile(0.0));
+  for (int i = 1; i <= 97; i++) {
+    EXPECT_EQ(base::TimeDelta::FromMilliseconds(i),
+              large_history.Percentile(i - 0.5));
+  }
+  EXPECT_EQ(sample1, large_history.Percentile(97.5));
+  EXPECT_EQ(sample3, large_history.Percentile(98.5));
+  EXPECT_EQ(sample2, large_history.Percentile(99.5));
+
+  // Continue inserting samples, causing the oldest samples to be discarded.
+  base::TimeDelta sample4 = base::TimeDelta::FromMilliseconds(100);
+  base::TimeDelta sample5 = base::TimeDelta::FromMilliseconds(102);
+  base::TimeDelta sample6 = base::TimeDelta::FromMilliseconds(104);
+  large_history.InsertSample(sample4);
+  large_history.InsertSample(sample5);
+  large_history.InsertSample(sample6);
+  EXPECT_EQ(sample4, large_history.Percentile(97.5));
+  EXPECT_EQ(sample5, large_history.Percentile(98.5));
+  EXPECT_EQ(sample6, large_history.Percentile(99.5));
+
+  large_history.Clear();
+  EXPECT_EQ(base::TimeDelta(), large_history.Percentile(0.0));
+  EXPECT_EQ(base::TimeDelta(), large_history.Percentile(50.0));
+  EXPECT_EQ(base::TimeDelta(), large_history.Percentile(100.0));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/scoped_ptr_algorithm.h b/cc/base/scoped_ptr_algorithm.h
new file mode 100644
index 0000000..79f4eee
--- /dev/null
+++ b/cc/base/scoped_ptr_algorithm.h
@@ -0,0 +1,30 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SCOPED_PTR_ALGORITHM_H_
+#define CC_BASE_SCOPED_PTR_ALGORITHM_H_
+
+namespace cc {
+
+// ScopedContainers need to implement a swap() method since they do not allow
+// assignment to their iterators.
+template <class ForwardIterator, class Predicate, class ScopedContainer>
+ForwardIterator remove_if(
+    ScopedContainer* container,
+    ForwardIterator first,
+    ForwardIterator last,
+    Predicate predicate) {
+  ForwardIterator result = first;
+  for (; first != last; ++first) {
+    if (!predicate(*first)) {
+      container->swap(first, result);
+      ++result;
+    }
+  }
+  return result;
+}
+
+}  // namespace cc
+
+#endif  // CC_BASE_SCOPED_PTR_ALGORITHM_H_
diff --git a/cc/base/scoped_ptr_deque.h b/cc/base/scoped_ptr_deque.h
new file mode 100644
index 0000000..cb4adfc
--- /dev/null
+++ b/cc/base/scoped_ptr_deque.h
@@ -0,0 +1,137 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SCOPED_PTR_DEQUE_H_
+#define CC_BASE_SCOPED_PTR_DEQUE_H_
+
+#include <algorithm>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+
+namespace cc {
+
+// This type acts like a deque<scoped_ptr> based on top of std::deque. The
+// ScopedPtrDeque has ownership of all elements in the deque.
+template <typename T>
+class ScopedPtrDeque {
+ public:
+  typedef typename std::deque<T*>::const_iterator const_iterator;
+  typedef typename std::deque<T*>::reverse_iterator reverse_iterator;
+  typedef typename std::deque<T*>::const_reverse_iterator
+      const_reverse_iterator;
+
+#if defined(OS_ANDROID)
+  // On Android the iterator is not a class, so we can't block assignment.
+  typedef typename std::deque<T*>::iterator iterator;
+#else
+  // Ban setting values on the iterator directly. New pointers must be passed
+  // to methods on the ScopedPtrDeque class to appear in the deque.
+  class iterator : public std::deque<T*>::iterator {
+   public:
+    explicit iterator(const typename std::deque<T*>::iterator& other)
+        : std::deque<T*>::iterator(other) {}
+    T* const& operator*() { return std::deque<T*>::iterator::operator*(); }
+  };
+#endif
+
+  ScopedPtrDeque() {}
+
+  ~ScopedPtrDeque() { clear(); }
+
+  size_t size() const {
+    return data_.size();
+  }
+
+  T* at(size_t index) const {
+    DCHECK(index < size());
+    return data_[index];
+  }
+
+  T* operator[](size_t index) const {
+    return at(index);
+  }
+
+  T* front() const {
+    DCHECK(!empty());
+    return at(0);
+  }
+
+  T* back() const {
+    DCHECK(!empty());
+    return at(size() - 1);
+  }
+
+  bool empty() const {
+    return data_.empty();
+  }
+
+  scoped_ptr<T> take_front() {
+    scoped_ptr<T> ret(front());
+    data_.pop_front();
+    return ret.Pass();
+  }
+
+  scoped_ptr<T> take_back() {
+    scoped_ptr<T> ret(back());
+    data_.pop_back();
+    return ret.Pass();
+  }
+
+  void clear() {
+    STLDeleteElements(&data_);
+  }
+
+  void push_front(scoped_ptr<T> item) {
+    data_.push_front(item.release());
+  }
+
+  void push_back(scoped_ptr<T> item) {
+    data_.push_back(item.release());
+  }
+
+  void insert(iterator position, scoped_ptr<T> item) {
+    DCHECK(position <= end());
+    data_.insert(position, item.release());
+  }
+
+  scoped_ptr<T> take(iterator position) {
+    DCHECK(position < end());
+    scoped_ptr<T> ret(*position);
+    data_.erase(position);
+    return ret.Pass();
+  }
+
+  void swap(iterator a, iterator b) {
+    DCHECK(a < end());
+    DCHECK(b < end());
+    if (a == end() || b == end() || a == b)
+      return;
+    typename std::deque<T*>::iterator writable_a = a;
+    typename std::deque<T*>::iterator writable_b = b;
+    std::swap(*writable_a, *writable_b);
+  }
+
+  iterator begin() { return static_cast<iterator>(data_.begin()); }
+  const_iterator begin() const { return data_.begin(); }
+  iterator end() { return static_cast<iterator>(data_.end()); }
+  const_iterator end() const { return data_.end(); }
+
+  reverse_iterator rbegin() { return data_.rbegin(); }
+  const_reverse_iterator rbegin() const { return data_.rbegin(); }
+  reverse_iterator rend() { return data_.rend(); }
+  const_reverse_iterator rend() const { return data_.rend(); }
+
+ private:
+  std::deque<T*> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPtrDeque);
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_SCOPED_PTR_DEQUE_H_
diff --git a/cc/base/scoped_ptr_vector.h b/cc/base/scoped_ptr_vector.h
new file mode 100644
index 0000000..0b83e35
--- /dev/null
+++ b/cc/base/scoped_ptr_vector.h
@@ -0,0 +1,200 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SCOPED_PTR_VECTOR_H_
+#define CC_BASE_SCOPED_PTR_VECTOR_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+
+namespace cc {
+
+// This type acts like a vector<scoped_ptr> based on top of std::vector. The
+// ScopedPtrVector has ownership of all elements in the vector.
+template <typename T>
+class ScopedPtrVector {
+ public:
+  typedef typename std::vector<T*>::const_iterator const_iterator;
+  typedef typename std::vector<T*>::reverse_iterator reverse_iterator;
+  typedef typename std::vector<T*>::const_reverse_iterator
+      const_reverse_iterator;
+
+#if defined(OS_ANDROID)
+  // On Android the iterator is not a class, so we can't block assignment.
+  typedef typename std::vector<T*>::iterator iterator;
+#else
+  // Ban setting values on the iterator directly. New pointers must be passed
+  // to methods on the ScopedPtrVector class to appear in the vector.
+  class iterator : public std::vector<T*>::iterator {
+   public:
+    iterator(const typename std::vector<T*>::iterator& other) // NOLINT
+        : std::vector<T*>::iterator(other) {}
+    T* const& operator*() { return std::vector<T*>::iterator::operator*(); }
+  };
+#endif
+
+  ScopedPtrVector() {}
+
+  ~ScopedPtrVector() { clear(); }
+
+  size_t size() const {
+    return data_.size();
+  }
+
+  T* at(size_t index) const {
+    DCHECK(index < size());
+    return data_[index];
+  }
+
+  T* operator[](size_t index) const {
+    return at(index);
+  }
+
+  T* front() const {
+    DCHECK(!empty());
+    return at(0);
+  }
+
+  T* back() const {
+    DCHECK(!empty());
+    return at(size() - 1);
+  }
+
+  bool empty() const {
+    return data_.empty();
+  }
+
+  scoped_ptr<T> take(iterator position) {
+    if (position == end())
+      return nullptr;
+    DCHECK(position < end());
+
+    typename std::vector<T*>::iterator writable_position = position;
+    scoped_ptr<T> ret(*writable_position);
+    *writable_position = NULL;
+    return ret.Pass();
+  }
+
+  scoped_ptr<T> take_back() {
+    DCHECK(!empty());
+    if (empty())
+      return nullptr;
+    return take(end() - 1);
+  }
+
+  void erase(iterator position) {
+    if (position == end())
+      return;
+    typename std::vector<T*>::iterator writable_position = position;
+    delete *writable_position;
+    data_.erase(position);
+  }
+
+  void erase(iterator first, iterator last) {
+    DCHECK(first <= last);
+    for (iterator it = first; it != last; ++it) {
+      DCHECK(it < end());
+
+      typename std::vector<T*>::iterator writable_it = it;
+      delete *writable_it;
+    }
+    data_.erase(first, last);
+  }
+
+  void reserve(size_t size) {
+    data_.reserve(size);
+  }
+
+  void clear() {
+    STLDeleteElements(&data_);
+  }
+
+  void push_back(scoped_ptr<T> item) {
+    data_.push_back(item.release());
+  }
+
+  void pop_back() {
+    delete data_.back();
+    data_.pop_back();
+  }
+
+  void insert(iterator position, scoped_ptr<T> item) {
+    DCHECK(position <= end());
+    data_.insert(position, item.release());
+  }
+
+  void insert_and_take(iterator position, ScopedPtrVector<T>* other) {
+    std::vector<T*> tmp_data;
+    for (ScopedPtrVector<T>::iterator it = other->begin(); it != other->end();
+         ++it) {
+      tmp_data.push_back(other->take(it).release());
+    }
+    data_.insert(position, tmp_data.begin(), tmp_data.end());
+  }
+
+  template <typename Predicate>
+  iterator partition(Predicate predicate) {
+    typename std::vector<T*>::iterator first = begin();
+    typename std::vector<T*>::iterator last = end();
+    return static_cast<iterator>(std::partition(first, last, predicate));
+  }
+
+  void swap(ScopedPtrVector<T>& other) {
+    data_.swap(other.data_);
+  }
+
+  void swap(iterator a, iterator b) {
+    DCHECK(a < end());
+    DCHECK(b < end());
+    if (a == end() || b == end() || a == b)
+      return;
+    typename std::vector<T*>::iterator writable_a = a;
+    typename std::vector<T*>::iterator writable_b = b;
+    std::swap(*writable_a, *writable_b);
+  }
+
+  template<class Compare>
+  inline void sort(Compare comp) {
+    std::sort(data_.begin(), data_.end(), comp);
+  }
+
+  template <class Compare>
+  inline void make_heap(Compare comp) {
+    std::make_heap(data_.begin(), data_.end(), comp);
+  }
+
+  template <class Compare>
+  inline void push_heap(Compare comp) {
+    std::push_heap(data_.begin(), data_.end(), comp);
+  }
+
+  template <class Compare>
+  inline void pop_heap(Compare comp) {
+    std::pop_heap(data_.begin(), data_.end(), comp);
+  }
+
+  iterator begin() { return static_cast<iterator>(data_.begin()); }
+  const_iterator begin() const { return data_.begin(); }
+  iterator end() { return static_cast<iterator>(data_.end()); }
+  const_iterator end() const { return data_.end(); }
+
+  reverse_iterator rbegin() { return data_.rbegin(); }
+  const_reverse_iterator rbegin() const { return data_.rbegin(); }
+  reverse_iterator rend() { return data_.rend(); }
+  const_reverse_iterator rend() const { return data_.rend(); }
+
+ private:
+  std::vector<T*> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPtrVector);
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_SCOPED_PTR_VECTOR_H_
diff --git a/cc/base/scoped_ptr_vector_unittest.cc b/cc/base/scoped_ptr_vector_unittest.cc
new file mode 100644
index 0000000..73a7f81
--- /dev/null
+++ b/cc/base/scoped_ptr_vector_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "cc/base/scoped_ptr_vector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cc {
+namespace {
+
+class Data {
+ public:
+  static scoped_ptr<Data> Create(int i) { return make_scoped_ptr(new Data(i)); }
+  int data() const { return data_; }
+ private:
+  explicit Data(int i) : data_(i) {}
+  int data_;
+};
+
+class IsOddPredicate {
+ public:
+  bool operator()(const Data* data) { return (data->data() % 2) == 1; }
+};
+
+TEST(ScopedPtrVectorTest, PushBack) {
+  ScopedPtrVector<Data> v;
+
+  // Insert 5 things into the vector.
+  v.push_back(Data::Create(1));
+  v.push_back(Data::Create(2));
+  v.push_back(Data::Create(3));
+  v.push_back(Data::Create(4));
+  v.push_back(Data::Create(5));
+
+  EXPECT_EQ(5u, v.size());
+  EXPECT_EQ(1, v[0]->data());
+  EXPECT_EQ(2, v[1]->data());
+  EXPECT_EQ(3, v[2]->data());
+  EXPECT_EQ(4, v[3]->data());
+  EXPECT_EQ(5, v[4]->data());
+}
+
+TEST(ScopedPtrVectorTest, InsertAndTake) {
+  // Insert 3 things into each vector.
+  ScopedPtrVector<Data> v;
+  v.push_back(Data::Create(1));
+  v.push_back(Data::Create(2));
+  v.push_back(Data::Create(6));
+
+  ScopedPtrVector<Data> v2;
+  v2.push_back(Data::Create(3));
+  v2.push_back(Data::Create(4));
+  v2.push_back(Data::Create(5));
+
+  ScopedPtrVector<Data>::iterator it = v.begin();
+  ++it;
+  ++it;
+  EXPECT_EQ(6, (*it)->data());
+
+  v.insert_and_take(it, &v2);
+
+  EXPECT_EQ(6u, v.size());
+  EXPECT_EQ(1, v[0]->data());
+  EXPECT_EQ(2, v[1]->data());
+  EXPECT_EQ(3, v[2]->data());
+  EXPECT_EQ(4, v[3]->data());
+  EXPECT_EQ(5, v[4]->data());
+  EXPECT_EQ(6, v[5]->data());
+
+  EXPECT_EQ(3u, v2.size());
+  EXPECT_EQ(NULL, v2[0]);
+  EXPECT_EQ(NULL, v2[1]);
+  EXPECT_EQ(NULL, v2[2]);
+}
+
+TEST(ScopedPtrVectorTest, Partition) {
+  ScopedPtrVector<Data> v;
+  v.push_back(Data::Create(1));
+  v.push_back(Data::Create(2));
+  v.push_back(Data::Create(3));
+  v.push_back(Data::Create(4));
+  v.push_back(Data::Create(5));
+
+  ScopedPtrVector<Data>::iterator it = v.partition(IsOddPredicate());
+  std::set<int> odd_numbers;
+  for (ScopedPtrVector<Data>::iterator second_it = v.begin();
+       second_it != it;
+       ++second_it) {
+    EXPECT_EQ(1, (*second_it)->data() % 2);
+    odd_numbers.insert((*second_it)->data());
+  }
+  EXPECT_EQ(3u, odd_numbers.size());
+
+  std::set<int> even_numbers;
+  for (; it != v.end(); ++it) {
+    EXPECT_EQ(0, (*it)->data() % 2);
+    even_numbers.insert((*it)->data());
+  }
+  EXPECT_EQ(2u, even_numbers.size());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/simple_enclosed_region.cc b/cc/base/simple_enclosed_region.cc
new file mode 100644
index 0000000..e2683f3
--- /dev/null
+++ b/cc/base/simple_enclosed_region.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/simple_enclosed_region.h"
+
+#include "base/logging.h"
+#include "cc/base/region.h"
+
+namespace cc {
+
+static bool RectIsLargerArea(const gfx::Rect& a, const gfx::Rect b) {
+  int64 a_area = static_cast<int64>(a.width()) * a.height();
+  int64 b_area = static_cast<int64>(b.width()) * b.height();
+  return a_area > b_area;
+}
+
+SimpleEnclosedRegion::SimpleEnclosedRegion(const Region& region) {
+  for (Region::Iterator it(region); it.has_rect(); it.next())
+    Union(it.rect());
+}
+
+SimpleEnclosedRegion::~SimpleEnclosedRegion() {
+}
+
+void SimpleEnclosedRegion::Subtract(const gfx::Rect& sub_rect) {
+  // We want to keep as much of the current rect as we can, so find the one
+  // largest rectangle inside |rect_| that does not intersect with |sub_rect|.
+  if (!rect_.Intersects(sub_rect))
+    return;
+  if (sub_rect.Contains(rect_)) {
+    rect_ = gfx::Rect();
+    return;
+  }
+
+  int left = rect_.x();
+  int right = rect_.right();
+  int top = rect_.y();
+  int bottom = rect_.bottom();
+
+  int delta_left = sub_rect.x() - left;
+  int delta_right = right - sub_rect.right();
+  int delta_top = sub_rect.y() - top;
+  int delta_bottom = bottom - sub_rect.bottom();
+
+  // The horizontal rect is the larger of the two rectangles above or below
+  // |sub_rect| and inside rect_.
+  int horizontal_top = top;
+  int horizontal_bottom = bottom;
+  if (delta_top > delta_bottom)
+    horizontal_bottom = sub_rect.y();
+  else
+    horizontal_top = sub_rect.bottom();
+  // The vertical rect is the larger of the two rectangles to the left or the
+  // right of |sub_rect| and inside rect_.
+  int vertical_left = left;
+  int vertical_right = right;
+  if (delta_left > delta_right)
+    vertical_right = sub_rect.x();
+  else
+    vertical_left = sub_rect.right();
+
+  rect_.SetRect(
+      left, horizontal_top, right - left, horizontal_bottom - horizontal_top);
+
+  gfx::Rect vertical_rect(
+      vertical_left, top, vertical_right - vertical_left, bottom - top);
+  if (RectIsLargerArea(vertical_rect, rect_))
+    rect_ = vertical_rect;
+}
+
+void SimpleEnclosedRegion::Union(const gfx::Rect& new_rect) {
+  // We want to keep track of a region but bound its complexity at a constant
+  // size. We keep track of the largest rectangle seen by area. If we can add
+  // the |new_rect| to this rectangle then we do that, as that is the cheapest
+  // way to increase the area returned without increasing the complexity.
+  if (new_rect.IsEmpty())
+    return;
+  if (rect_.Contains(new_rect))
+    return;
+  if (new_rect.Contains(rect_)) {
+    rect_ = new_rect;
+    return;
+  }
+
+  int left = rect_.x();
+  int top = rect_.y();
+  int right = rect_.right();
+  int bottom = rect_.bottom();
+
+  int new_left = new_rect.x();
+  int new_top = new_rect.y();
+  int new_right = new_rect.right();
+  int new_bottom = new_rect.bottom();
+
+  // This attempts to expand each edge of |rect_| if the |new_rect| entirely
+  // covers or is adjacent to an entire edge of |rect_|. If this is true for
+  // an edge of |rect_| then it can be expanded out to share that edge with the
+  // same edge of |new_rect|. After, the same thing is done to try expand
+  // |new_rect| relative to |rect_|.
+  if (new_top <= top && new_bottom >= bottom) {
+    if (new_left < left && new_right >= left)
+      left = new_left;
+    if (new_right > right && new_left <= right)
+      right = new_right;
+  } else if (new_left <= left && new_right >= right) {
+    if (new_top < top && new_bottom >= top)
+      top = new_top;
+    if (new_bottom > bottom && new_top <= bottom)
+      bottom = new_bottom;
+  } else if (top <= new_top && bottom >= new_bottom) {
+    if (left < new_left && right >= new_left)
+      new_left = left;
+    if (right > new_right && left <= new_right)
+      new_right = right;
+  } else if (left <= new_left && right >= new_right) {
+    if (top < new_top && bottom >= new_top)
+      new_top = top;
+    if (bottom > new_bottom && top <= new_bottom)
+      new_bottom = bottom;
+  }
+
+  rect_.SetRect(left, top, right - left, bottom - top);
+
+  gfx::Rect adjusted_new_rect(
+      new_left, new_top, new_right - new_left, new_bottom - new_top);
+  if (RectIsLargerArea(adjusted_new_rect, rect_))
+    rect_ = adjusted_new_rect;
+}
+
+gfx::Rect SimpleEnclosedRegion::GetRect(size_t i) const {
+  DCHECK_LT(i, GetRegionComplexity());
+  return rect_;
+}
+
+}  // namespace cc
diff --git a/cc/base/simple_enclosed_region.h b/cc/base/simple_enclosed_region.h
new file mode 100644
index 0000000..c9625ba
--- /dev/null
+++ b/cc/base/simple_enclosed_region.h
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SIMPLE_ENCLOSED_REGION_H_
+#define CC_BASE_SIMPLE_ENCLOSED_REGION_H_
+
+#include <string>
+
+#include "cc/base/cc_export.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+
+class Region;
+
+// A constant-sized approximation of a Region. The SimpleEnclosedRegion may
+// exclude points in its approximation (may have false negatives) but will never
+// include a point that would not be in the actual Region (no false positives).
+class CC_EXPORT SimpleEnclosedRegion {
+ public:
+  SimpleEnclosedRegion() : rect_() {}
+  SimpleEnclosedRegion(const SimpleEnclosedRegion& region)
+      : rect_(region.rect_) {}
+  explicit SimpleEnclosedRegion(const gfx::Rect& rect) : rect_(rect) {}
+  SimpleEnclosedRegion(int x, int y, int w, int h) : rect_(x, y, w, h) {}
+  SimpleEnclosedRegion(int w, int h) : rect_(w, h) {}
+  explicit SimpleEnclosedRegion(const Region& region);
+  ~SimpleEnclosedRegion();
+
+  const SimpleEnclosedRegion& operator=(const gfx::Rect& rect) {
+    rect_ = rect;
+    return *this;
+  }
+  const SimpleEnclosedRegion& operator=(const SimpleEnclosedRegion& region) {
+    rect_ = region.rect_;
+    return *this;
+  }
+
+  bool IsEmpty() const { return rect_.IsEmpty(); }
+  void Clear() { rect_ = gfx::Rect(); }
+  size_t GetRegionComplexity() const { return rect_.IsEmpty() ? 0 : 1; }
+
+  bool Contains(const gfx::Point& point) const { return rect_.Contains(point); }
+  bool Contains(const gfx::Rect& rect) const { return rect_.Contains(rect); }
+  bool Contains(const SimpleEnclosedRegion& region) const {
+    return rect_.Contains(region.rect_);
+  }
+
+  bool Intersects(const gfx::Rect& rect) const {
+    return rect_.Intersects(rect);
+  }
+  bool Intersects(const SimpleEnclosedRegion& region) const {
+    return rect_.Intersects(region.rect_);
+  }
+
+  void Subtract(const gfx::Rect& sub_rect);
+  void Subtract(const SimpleEnclosedRegion& sub_region) {
+    Subtract(sub_region.rect_);
+  }
+  void Union(const gfx::Rect& new_rect);
+  void Union(const SimpleEnclosedRegion& new_region) {
+    Union(new_region.rect_);
+  }
+  void Intersect(const gfx::Rect& in_rect) { return rect_.Intersect(in_rect); }
+  void Intersect(const SimpleEnclosedRegion& in_region) {
+    Intersect(in_region.rect_);
+  }
+
+  bool Equals(const SimpleEnclosedRegion& other) const {
+    bool both_empty = rect_.IsEmpty() && other.rect_.IsEmpty();
+    return both_empty || rect_ == other.rect_;
+  }
+
+  gfx::Rect bounds() const { return rect_; }
+
+  // The value of |i| must be less than GetRegionComplexity().
+  gfx::Rect GetRect(size_t i) const;
+
+  std::string ToString() const { return rect_.ToString(); }
+
+ private:
+  gfx::Rect rect_;
+};
+
+inline bool operator==(const SimpleEnclosedRegion& a,
+                       const SimpleEnclosedRegion& b) {
+  return a.Equals(b);
+}
+
+inline bool operator!=(const SimpleEnclosedRegion& a,
+                       const SimpleEnclosedRegion& b) {
+  return !(a == b);
+}
+
+inline SimpleEnclosedRegion SubtractSimpleEnclosedRegions(
+    const SimpleEnclosedRegion& a,
+    const SimpleEnclosedRegion& b) {
+  SimpleEnclosedRegion result = a;
+  result.Subtract(b);
+  return result;
+}
+
+inline SimpleEnclosedRegion IntersectSimpleEnclosedRegions(
+    const SimpleEnclosedRegion& a,
+    const SimpleEnclosedRegion& b) {
+  SimpleEnclosedRegion result = a;
+  result.Intersect(b);
+  return result;
+}
+
+inline SimpleEnclosedRegion UnionSimpleEnclosedRegions(
+    const SimpleEnclosedRegion& a,
+    const SimpleEnclosedRegion& b) {
+  SimpleEnclosedRegion result = a;
+  result.Union(b);
+  return result;
+}
+
+}  // namespace cc
+
+#endif  // CC_BASE_SIMPLE_ENCLOSED_REGION_H_
diff --git a/cc/base/simple_enclosed_region_unittest.cc b/cc/base/simple_enclosed_region_unittest.cc
new file mode 100644
index 0000000..869da4e
--- /dev/null
+++ b/cc/base/simple_enclosed_region_unittest.cc
@@ -0,0 +1,637 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/simple_enclosed_region.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "cc/base/region.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+bool ExpectRegionEq(const gfx::Rect& rect, const SimpleEnclosedRegion& region) {
+  std::vector<gfx::Rect> actual_rects;
+  std::vector<gfx::Rect> expected_rects;
+
+  if (!rect.IsEmpty())
+    expected_rects.push_back(rect);
+
+  for (size_t i = 0; i < region.GetRegionComplexity(); ++i)
+    actual_rects.push_back(region.GetRect(i));
+
+  if (rect.IsEmpty() != region.IsEmpty()) {
+    LOG(ERROR) << "Expected: " << rect.IsEmpty()
+               << " Actual: " << region.IsEmpty();
+    return false;
+  }
+
+  if (expected_rects.size() != actual_rects.size()) {
+    LOG(ERROR) << "Expected: " << expected_rects.size()
+               << " Actual: " << actual_rects.size();
+    return false;
+  }
+
+  std::sort(actual_rects.begin(), actual_rects.end());
+  std::sort(expected_rects.begin(), expected_rects.end());
+
+  for (size_t i = 0; i < expected_rects.size(); ++i) {
+    if (expected_rects[i] != actual_rects[i]) {
+      LOG(ERROR) << "Expected: " << expected_rects[i].ToString()
+                 << " Actual: " << actual_rects[i].ToString();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+TEST(SimpleEnclosedRegionTest, Create) {
+  SimpleEnclosedRegion r1;
+  EXPECT_TRUE(r1.IsEmpty());
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r1));
+
+  SimpleEnclosedRegion r2(gfx::Rect(2, 3, 4, 5));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r2));
+
+  SimpleEnclosedRegion r3(2, 3, 4, 5);
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r3));
+
+  SimpleEnclosedRegion r4(4, 5);
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5), r4));
+
+  SimpleEnclosedRegion r5(Region(gfx::Rect(2, 3, 4, 5)));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r5));
+
+  SimpleEnclosedRegion r6(r5);
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r6));
+}
+
+TEST(SimpleEnclosedRegionTest, Assign) {
+  SimpleEnclosedRegion r;
+  EXPECT_TRUE(r.IsEmpty());
+
+  r = gfx::Rect(2, 3, 4, 5);
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r));
+
+  r = SimpleEnclosedRegion(3, 4, 5, 6);
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(3, 4, 5, 6), r));
+}
+
+TEST(SimpleEnclosedRegionTest, Clear) {
+  SimpleEnclosedRegion r(1, 2, 3, 4);
+  EXPECT_FALSE(r.IsEmpty());
+  r.Clear();
+  EXPECT_TRUE(r.IsEmpty());
+}
+
+TEST(SimpleEnclosedRegionTest, GetRegionComplexity) {
+  SimpleEnclosedRegion empty;
+  EXPECT_EQ(0u, empty.GetRegionComplexity());
+
+  SimpleEnclosedRegion stuff;
+  stuff.Union(gfx::Rect(1, 2, 3, 4));
+  EXPECT_EQ(1u, stuff.GetRegionComplexity());
+
+  // The SimpleEnclosedRegion only holds up to 1 rect.
+  stuff.Union(gfx::Rect(5, 6, 7, 8));
+  EXPECT_EQ(1u, stuff.GetRegionComplexity());
+}
+
+TEST(SimpleEnclosedRegionTest, Contains) {
+  SimpleEnclosedRegion r(1, 2, 5, 6);
+
+  EXPECT_FALSE(r.Contains(gfx::Point(0, 2)));
+  EXPECT_FALSE(r.Contains(gfx::Point(1, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Point(1, 2)));
+
+  EXPECT_FALSE(r.Contains(gfx::Point(6, 2)));
+  EXPECT_FALSE(r.Contains(gfx::Point(5, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Point(5, 2)));
+
+  EXPECT_FALSE(r.Contains(gfx::Point(0, 7)));
+  EXPECT_FALSE(r.Contains(gfx::Point(1, 8)));
+  EXPECT_TRUE(r.Contains(gfx::Point(1, 7)));
+
+  EXPECT_FALSE(r.Contains(gfx::Point(6, 7)));
+  EXPECT_FALSE(r.Contains(gfx::Point(5, 8)));
+  EXPECT_TRUE(r.Contains(gfx::Point(5, 7)));
+
+  EXPECT_FALSE(r.Contains(gfx::Rect(0, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(1, 1, 1, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Rect(1, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(0, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(gfx::Rect(6, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(5, 1, 1, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Rect(5, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(5, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(gfx::Rect(0, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(1, 8, 1, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Rect(1, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(0, 7, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(gfx::Rect(6, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(5, 8, 1, 1)));
+  EXPECT_TRUE(r.Contains(gfx::Rect(5, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(gfx::Rect(5, 7, 2, 2)));
+
+  gfx::Rect q(1, 2, 5, 6);
+  EXPECT_TRUE(r.Contains(q)) << q.ToString();
+  q.Inset(-1, 0, 0, 0);
+  EXPECT_FALSE(r.Contains(q)) << q.ToString();
+  q.Inset(1, -1, 0, 0);
+  EXPECT_FALSE(r.Contains(q)) << q.ToString();
+  q.Inset(0, 1, -1, 0);
+  EXPECT_FALSE(r.Contains(q)) << q.ToString();
+  q.Inset(0, 0, 1, -1);
+  EXPECT_FALSE(r.Contains(q)) << q.ToString();
+
+  q.Inset(1, 0, 0, 1);
+  EXPECT_TRUE(r.Contains(q)) << q.ToString();
+  q.Inset(-1, 1, 0, 0);
+  EXPECT_TRUE(r.Contains(q)) << q.ToString();
+  q.Inset(0, -1, 1, 0);
+  EXPECT_TRUE(r.Contains(q)) << q.ToString();
+  q.Inset(0, 0, -1, 1);
+  EXPECT_TRUE(r.Contains(q)) << q.ToString();
+
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(1, 1, 1, 1)));
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(1, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(6, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 1, 1, 1)));
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(5, 2, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(1, 8, 1, 1)));
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(1, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 7, 2, 2)));
+
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(6, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 8, 1, 1)));
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(5, 7, 1, 1)));
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 7, 2, 2)));
+
+  q = gfx::Rect(1, 2, 5, 6);
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(-1, 0, 0, 0);
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(1, -1, 0, 0);
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 1, -1, 0);
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 0, 1, -1);
+  EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+
+  q.Inset(1, 0, 0, 1);
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(-1, 1, 0, 0);
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, -1, 1, 0);
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 0, -1, 1);
+  EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString();
+}
+
+TEST(SimpleEnclosedRegionTest, Intersects) {
+  SimpleEnclosedRegion r(1, 2, 5, 6);
+
+  EXPECT_FALSE(r.Intersects(gfx::Rect(0, 2, 1, 1)));
+  EXPECT_FALSE(r.Intersects(gfx::Rect(1, 1, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(1, 2, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(0, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(gfx::Rect(6, 2, 1, 1)));
+  EXPECT_FALSE(r.Intersects(gfx::Rect(5, 1, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(5, 2, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(5, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(gfx::Rect(0, 7, 1, 1)));
+  EXPECT_FALSE(r.Intersects(gfx::Rect(1, 8, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(1, 7, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(0, 7, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(gfx::Rect(6, 7, 1, 1)));
+  EXPECT_FALSE(r.Intersects(gfx::Rect(5, 8, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(5, 7, 1, 1)));
+  EXPECT_TRUE(r.Intersects(gfx::Rect(5, 7, 2, 2)));
+
+  gfx::Rect q(1, 2, 5, 6);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(-1, 0, 0, 0);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(1, -1, 0, 0);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(0, 1, -1, 0);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(0, 0, 1, -1);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+
+  q.Inset(1, 0, 0, 1);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(-1, 1, 0, 0);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(0, -1, 1, 0);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+  q.Inset(0, 0, -1, 1);
+  EXPECT_TRUE(r.Intersects(q)) << q.ToString();
+
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(0, 2, 1, 1)));
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(1, 1, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(1, 2, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(0, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(6, 2, 1, 1)));
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(5, 1, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 2, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 1, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(0, 7, 1, 1)));
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(1, 8, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(1, 7, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(0, 7, 2, 2)));
+
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(6, 7, 1, 1)));
+  EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(5, 8, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 7, 1, 1)));
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 7, 2, 2)));
+
+  q = gfx::Rect(1, 2, 5, 6);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(-1, 0, 0, 0);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(1, -1, 0, 0);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 1, -1, 0);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 0, 1, -1);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+
+  q.Inset(1, 0, 0, 1);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(-1, 1, 0, 0);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, -1, 1, 0);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+  q.Inset(0, 0, -1, 1);
+  EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString();
+}
+
+TEST(SimpleEnclosedRegionTest, Equals) {
+  SimpleEnclosedRegion r(1, 2, 3, 4);
+  EXPECT_TRUE(r.Equals(SimpleEnclosedRegion(1, 2, 3, 4)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(2, 2, 3, 4)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 3, 3, 4)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 2, 4, 4)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 2, 3, 5)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(2, 2, 2, 4)));
+  EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 3, 3, 3)));
+}
+
+TEST(SimpleEnclosedRegionTest, Bounds) {
+  SimpleEnclosedRegion r;
+  EXPECT_EQ(gfx::Rect(), r.bounds());
+  r = gfx::Rect(3, 4, 5, 6);
+  EXPECT_EQ(gfx::Rect(3, 4, 5, 6), r.bounds());
+  r.Union(gfx::Rect(1, 2, 12, 13));
+  EXPECT_EQ(gfx::Rect(1, 2, 12, 13), r.bounds());
+}
+
+TEST(SimpleEnclosedRegionTest, GetRect) {
+  SimpleEnclosedRegion r(3, 4, 5, 6);
+  EXPECT_EQ(gfx::Rect(3, 4, 5, 6), r.GetRect(0));
+  r.Union(gfx::Rect(1, 2, 12, 13));
+  EXPECT_EQ(gfx::Rect(1, 2, 12, 13), r.GetRect(0));
+}
+
+TEST(SimpleEnclosedRegionTest, Union) {
+  SimpleEnclosedRegion r;
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r));
+
+  // Empty Union anything = anything.
+  r.Union(gfx::Rect(4, 5, 6, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r));
+
+  // Anything Union empty = anything.
+  r.Union(gfx::Rect());
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r));
+
+  // Anything Union contained rect = Anything.
+  r.Union(gfx::Rect(5, 6, 4, 5));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r));
+
+  // Anything Union containing rect = containing rect.
+  r.Union(gfx::Rect(2, 3, 8, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 8, 9), r));
+  r.Union(gfx::Rect(2, 3, 9, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r));
+
+  // Union with a smaller disjoint rect is ignored.
+  r.Union(gfx::Rect(20, 21, 9, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r));
+
+  // Union with a smaller overlapping rect is ignored.
+  r.Union(gfx::Rect(3, 4, 9, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r));
+
+  // Union with an equal sized rect can be either one.
+  r.Union(gfx::Rect(4, 4, 9, 10));
+  EXPECT_EQ(1u, r.GetRegionComplexity());
+  EXPECT_TRUE(r.bounds() == gfx::Rect(2, 3, 9, 10) ||
+              r.bounds() == gfx::Rect(4, 4, 9, 10));
+
+  // Union with a larger disjoint rect is taken.
+  r.Union(gfx::Rect(20, 21, 12, 13));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(20, 21, 12, 13), r));
+
+  // Union with a larger overlapping rect is taken.
+  r.Union(gfx::Rect(19, 19, 12, 14));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(19, 19, 12, 14), r));
+
+  // True also when the rect covers one edge of the existing region.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(12, 7, 9, 16));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 7, 9, 16), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(9, 7, 9, 16));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 7, 9, 16), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(7, 12, 16, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 12, 16, 9), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(7, 9, 16, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 9, 16, 9), r));
+
+  // But if the existing region can be expanded to make a larger rect, then it
+  // will. Union area is 9*12 = 108. By merging, we make a rect with an area of
+  // 10*11 = 110. The resulting rect is expanded as far as possible while
+  // remaining enclosed in the Union.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(12, 9, 9, 12));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(9, 9, 9, 12));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(9, 12, 12, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Union(gfx::Rect(9, 9, 12, 9));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r));
+
+  r = gfx::Rect(12, 9, 9, 12);
+  r.Union(gfx::Rect(10, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r));
+
+  r = gfx::Rect(9, 9, 9, 12);
+  r.Union(gfx::Rect(10, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r));
+
+  r = gfx::Rect(9, 12, 12, 9);
+  r.Union(gfx::Rect(10, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r));
+
+  r = gfx::Rect(9, 9, 12, 9);
+  r.Union(gfx::Rect(10, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r));
+}
+
+TEST(SimpleEnclosedRegionTest, Subtract) {
+  SimpleEnclosedRegion r;
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r));
+
+  // Empty Subtract anything = empty.
+  r.Subtract(gfx::Rect(4, 5, 6, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r));
+
+  // Subtracting an enclosing rect = empty.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(9, 9, 12, 12));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r));
+
+  // Subtracting a rect that covers one side of the region will shrink that
+  // side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(18, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(18, 8, 10, 14));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 18, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(8, 18, 14, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(2, 10, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(2, 8, 10, 14));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 2, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(8, 2, 14, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r));
+
+  // Subtracting a rect that does not cover a full side will still shrink that
+  // side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(18, 12, 10, 8));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(18, 12, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 18, 8, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 18, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(2, 12, 10, 8));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(2, 12, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 2, 8, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 2, 10, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r));
+
+  // Subtracting a rect inside the region will make it choose the larger result.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(11, 11, 7, 8));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(18, 10, 2, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(11, 11, 8, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 18, 10, 2), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 11, 7, 8));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 2, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(11, 12, 8, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 2), r));
+
+  // Subtracting a rect that cuts the region in two will choose the larger side.
+  // Here it's the top side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 14, 10, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(0, 14, 30, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 14, 8, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(0, 14, 18, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 14, 18, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  // Here it's the bottom side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 13, 10, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(0, 13, 30, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 13, 8, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(0, 13, 18, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(12, 13, 18, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r));
+
+  // Here it's the left side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  // Here it's the right side.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 10, 3, 10));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r));
+
+  // Subtracting a rect that leaves three possible choices will choose the
+  // larger.
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 14, 7, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(10, 14, 5, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(15, 10, 5, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(13, 14, 7, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(15, 14, 5, 3));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 5, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 10, 3, 5));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 15, 10, 5), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 13, 3, 7));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r));
+
+  r = gfx::Rect(10, 10, 10, 10);
+  r.Subtract(gfx::Rect(14, 15, 3, 5));
+  EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 5), r));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/swap_promise.h b/cc/base/swap_promise.h
new file mode 100644
index 0000000..a406fda
--- /dev/null
+++ b/cc/base/swap_promise.h
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SWAP_PROMISE_H_
+#define CC_BASE_SWAP_PROMISE_H_
+
+#include "cc/output/compositor_frame_metadata.h"
+
+namespace cc {
+
+// When a change to the compositor's state/invalidation/whatever happens, a
+// Swap Promise can be inserted into LayerTreeHost/LayerTreeImpl, to track
+// whether the compositor's reply to the new state/invaliadtion/whatever is
+// completed in the compositor, i.e. the compositor knows it has been sent
+// to its output or not.
+//
+// If the new compositor state is sent to the output, SwapPromise::DidSwap()
+// will be called, and if the compositor fails to send its new state to the
+// output, SwapPromise::DidNotSwap() will be called.
+//
+// Client wishes to use SwapPromise should have a subclass that defines
+// the behavior of DidSwap() and DidNotSwap(). Notice that the promise can
+// be broken at either main or impl thread, e.g. commit fails on main thread,
+// new frame data has no actual damage so LayerTreeHostImpl::SwapBuffers()
+// bails out early on impl thread, so don't assume that DidSwap() and
+// DidNotSwap() are called at a particular thread. It is better to let the
+// subclass carry thread-safe member data and operate on that member data in
+// DidSwap() and DidNotSwap().
+class CC_EXPORT SwapPromise {
+ public:
+  enum DidNotSwapReason {
+    DID_NOT_SWAP_UNKNOWN,
+    SWAP_FAILS,
+    COMMIT_FAILS,
+    COMMIT_NO_UPDATE,
+  };
+
+  SwapPromise() {}
+  virtual ~SwapPromise() {}
+
+  virtual void DidSwap(CompositorFrameMetadata* metadata) = 0;
+  virtual void DidNotSwap(DidNotSwapReason reason) = 0;
+
+  // A non-zero trace id identifies a trace flow object that is embedded in the
+  // swap promise. This can be used for registering additional flow steps to
+  // visualize the object's path through the system.
+  virtual int64 TraceId() const = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_SWAP_PROMISE_H_
diff --git a/cc/base/swap_promise_monitor.cc b/cc/base/swap_promise_monitor.cc
new file mode 100644
index 0000000..0c04f35
--- /dev/null
+++ b/cc/base/swap_promise_monitor.cc
@@ -0,0 +1,31 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "cc/base/swap_promise_monitor.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_host_impl.h"
+
+namespace cc {
+
+SwapPromiseMonitor::SwapPromiseMonitor(LayerTreeHost* layer_tree_host,
+                                       LayerTreeHostImpl* layer_tree_host_impl)
+    : layer_tree_host_(layer_tree_host),
+      layer_tree_host_impl_(layer_tree_host_impl) {
+  DCHECK((layer_tree_host && !layer_tree_host_impl) ||
+         (!layer_tree_host && layer_tree_host_impl));
+  if (layer_tree_host_)
+    layer_tree_host_->InsertSwapPromiseMonitor(this);
+  if (layer_tree_host_impl_)
+    layer_tree_host_impl_->InsertSwapPromiseMonitor(this);
+}
+
+SwapPromiseMonitor::~SwapPromiseMonitor() {
+  if (layer_tree_host_)
+    layer_tree_host_->RemoveSwapPromiseMonitor(this);
+  if (layer_tree_host_impl_)
+    layer_tree_host_impl_->RemoveSwapPromiseMonitor(this);
+}
+
+}  // namespace cc
diff --git a/cc/base/swap_promise_monitor.h b/cc/base/swap_promise_monitor.h
new file mode 100644
index 0000000..b8c8cd0
--- /dev/null
+++ b/cc/base/swap_promise_monitor.h
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_SWAP_PROMISE_MONITOR_H_
+#define CC_BASE_SWAP_PROMISE_MONITOR_H_
+
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class LayerTreeHost;
+class LayerTreeHostImpl;
+
+// A SwapPromiseMonitor is used to monitor compositor state change that
+// should be associated with a SwapPromise, e.g. SetNeedsCommit() is
+// called on main thread or SetNeedsRedraw() is called on impl thread.
+// Creating a SwapPromiseMonitor will insert itself into a LayerTreeHost
+// or LayerTreeHostImpl. You must provide a pointer to the appropriate
+// structure to the monitor (and only one of the two). Notification of
+// compositor state change will be sent through OnSetNeedsCommitOnMain()
+// or OnSetNeedsRedrawOnImpl(). When SwapPromiseMonitor is destroyed, it
+// will unregister itself from LayerTreeHost or LayerTreeHostImpl.
+class CC_EXPORT SwapPromiseMonitor {
+ public:
+  // If the monitor lives on the main thread, pass in layer_tree_host
+  // and set layer_tree_host_impl to NULL.
+  // If the monitor lives on the impl thread, pass in layer_tree_host_impl
+  // and set layer_tree_host to NULL.
+  SwapPromiseMonitor(LayerTreeHost* layer_tree_host,
+                     LayerTreeHostImpl* layer_tree_host_impl);
+  virtual ~SwapPromiseMonitor();
+
+  virtual void OnSetNeedsCommitOnMain() = 0;
+  virtual void OnSetNeedsRedrawOnImpl() = 0;
+  virtual void OnForwardScrollUpdateToMainThreadOnImpl() = 0;
+
+ protected:
+  LayerTreeHost* layer_tree_host_;
+  LayerTreeHostImpl* layer_tree_host_impl_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_SWAP_PROMISE_MONITOR_H_
diff --git a/cc/base/switches.cc b/cc/base/switches.cc
new file mode 100644
index 0000000..5bfa6ae
--- /dev/null
+++ b/cc/base/switches.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/switches.h"
+
+#include "base/command_line.h"
+
+namespace cc {
+namespace switches {
+
+const char kDisableThreadedAnimation[] = "disable-threaded-animation";
+
+// Disables layer-edge anti-aliasing in the compositor.
+const char kDisableCompositedAntialiasing[] =
+    "disable-composited-antialiasing";
+
+// Disables sending the next BeginMainFrame before the previous commit
+// activates. Overrides the kEnableMainFrameBeforeActivation flag.
+const char kDisableMainFrameBeforeActivation[] =
+    "disable-main-frame-before-activation";
+
+// Enables sending the next BeginMainFrame before the previous commit activates.
+const char kEnableMainFrameBeforeActivation[] =
+    "enable-main-frame-before-activation";
+
+const char kEnableTopControlsPositionCalculation[] =
+    "enable-top-controls-position-calculation";
+
+// The height of the movable top controls.
+const char kTopControlsHeight[] = "top-controls-height";
+
+// Percentage of the top controls need to be hidden before they will auto hide.
+const char kTopControlsHideThreshold[] = "top-controls-hide-threshold";
+
+// Percentage of the top controls need to be shown before they will auto show.
+const char kTopControlsShowThreshold[] = "top-controls-show-threshold";
+
+// Re-rasters everything multiple times to simulate a much slower machine.
+// Give a scale factor to cause raster to take that many times longer to
+// complete, such as --slow-down-raster-scale-factor=25.
+const char kSlowDownRasterScaleFactor[] = "slow-down-raster-scale-factor";
+
+// Max tiles allowed for each tilings interest area.
+const char kMaxTilesForInterestArea[] = "max-tiles-for-interest-area";
+
+// The amount of unused resource memory compositor is allowed to keep around.
+const char kMaxUnusedResourceMemoryUsagePercentage[] =
+    "max-unused-resource-memory-usage-percentage";
+
+// Causes the compositor to render to textures which are then sent to the parent
+// through the texture mailbox mechanism.
+// Requires --enable-compositor-frame-message.
+const char kCompositeToMailbox[] = "composite-to-mailbox";
+
+// Check that property changes during paint do not occur.
+const char kStrictLayerPropertyChangeChecking[] =
+    "strict-layer-property-change-checking";
+
+// Virtual viewport for fixed-position elements, scrollbars during pinch.
+const char kEnablePinchVirtualViewport[] = "enable-pinch-virtual-viewport";
+const char kDisablePinchVirtualViewport[] = "disable-pinch-virtual-viewport";
+
+// Disable partial swap which is needed for some OpenGL drivers / emulators.
+const char kUIDisablePartialSwap[] = "ui-disable-partial-swap";
+
+// Enables the GPU benchmarking extension
+const char kEnableGpuBenchmarking[] = "enable-gpu-benchmarking";
+
+// Renders a border around compositor layers to help debug and study
+// layer compositing.
+const char kShowCompositedLayerBorders[] = "show-composited-layer-borders";
+const char kUIShowCompositedLayerBorders[] = "ui-show-layer-borders";
+
+// Draws a FPS indicator
+const char kShowFPSCounter[] = "show-fps-counter";
+const char kUIShowFPSCounter[] = "ui-show-fps-counter";
+
+// Renders a border that represents the bounding box for the layer's animation.
+const char kShowLayerAnimationBounds[] = "show-layer-animation-bounds";
+const char kUIShowLayerAnimationBounds[] = "ui-show-layer-animation-bounds";
+
+// Show rects in the HUD around layers whose properties have changed.
+const char kShowPropertyChangedRects[] = "show-property-changed-rects";
+const char kUIShowPropertyChangedRects[] = "ui-show-property-changed-rects";
+
+// Show rects in the HUD around damage as it is recorded into each render
+// surface.
+const char kShowSurfaceDamageRects[] = "show-surface-damage-rects";
+const char kUIShowSurfaceDamageRects[] = "ui-show-surface-damage-rects";
+
+// Show rects in the HUD around the screen-space transformed bounds of every
+// layer.
+const char kShowScreenSpaceRects[] = "show-screenspace-rects";
+const char kUIShowScreenSpaceRects[] = "ui-show-screenspace-rects";
+
+// Show rects in the HUD around the screen-space transformed bounds of every
+// layer's replica, when they have one.
+const char kShowReplicaScreenSpaceRects[] = "show-replica-screenspace-rects";
+const char kUIShowReplicaScreenSpaceRects[] =
+    "ui-show-replica-screenspace-rects";
+
+// Show rects in the HUD wherever something is known to be drawn opaque and is
+// considered occluding the pixels behind it.
+const char kShowOccludingRects[] = "show-occluding-rects";
+const char kUIShowOccludingRects[] = "ui-show-occluding-rects";
+
+// Show rects in the HUD wherever something is not known to be drawn opaque and
+// is not considered to be occluding the pixels behind it.
+const char kShowNonOccludingRects[] = "show-nonoccluding-rects";
+const char kUIShowNonOccludingRects[] = "ui-show-nonoccluding-rects";
+
+// Prevents the layer tree unit tests from timing out.
+const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
+
+// Makes pixel tests write their output instead of read it.
+const char kCCRebaselinePixeltests[] = "cc-rebaseline-pixeltests";
+
+}  // namespace switches
+}  // namespace cc
diff --git a/cc/base/switches.h b/cc/base/switches.h
new file mode 100644
index 0000000..bbf680e
--- /dev/null
+++ b/cc/base/switches.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines all the "cc" command-line switches.
+
+#ifndef CC_BASE_SWITCHES_H_
+#define CC_BASE_SWITCHES_H_
+
+#include "cc/base/cc_export.h"
+
+// Since cc is used from the render process, anything that goes here also needs
+// to be added to render_process_host_impl.cc.
+
+namespace cc {
+namespace switches {
+
+// Switches for the renderer compositor only.
+CC_EXPORT extern const char kDisableThreadedAnimation[];
+CC_EXPORT extern const char kDisableCompositedAntialiasing[];
+CC_EXPORT extern const char kDisableMainFrameBeforeActivation[];
+CC_EXPORT extern const char kEnableMainFrameBeforeActivation[];
+CC_EXPORT extern const char kEnableTopControlsPositionCalculation[];
+CC_EXPORT extern const char kJankInsteadOfCheckerboard[];
+CC_EXPORT extern const char kTopControlsHeight[];
+CC_EXPORT extern const char kTopControlsHideThreshold[];
+CC_EXPORT extern const char kTopControlsShowThreshold[];
+CC_EXPORT extern const char kSlowDownRasterScaleFactor[];
+CC_EXPORT extern const char kCompositeToMailbox[];
+CC_EXPORT extern const char kMaxTilesForInterestArea[];
+CC_EXPORT extern const char kMaxUnusedResourceMemoryUsagePercentage[];
+CC_EXPORT extern const char kEnablePinchVirtualViewport[];
+CC_EXPORT extern const char kDisablePinchVirtualViewport[];
+CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[];
+
+// Switches for both the renderer and ui compositors.
+CC_EXPORT extern const char kUIDisablePartialSwap[];
+CC_EXPORT extern const char kEnableGpuBenchmarking[];
+
+// Debug visualizations.
+CC_EXPORT extern const char kShowCompositedLayerBorders[];
+CC_EXPORT extern const char kUIShowCompositedLayerBorders[];
+CC_EXPORT extern const char kShowFPSCounter[];
+CC_EXPORT extern const char kUIShowFPSCounter[];
+CC_EXPORT extern const char kShowLayerAnimationBounds[];
+CC_EXPORT extern const char kUIShowLayerAnimationBounds[];
+CC_EXPORT extern const char kShowPropertyChangedRects[];
+CC_EXPORT extern const char kUIShowPropertyChangedRects[];
+CC_EXPORT extern const char kShowSurfaceDamageRects[];
+CC_EXPORT extern const char kUIShowSurfaceDamageRects[];
+CC_EXPORT extern const char kShowScreenSpaceRects[];
+CC_EXPORT extern const char kUIShowScreenSpaceRects[];
+CC_EXPORT extern const char kShowReplicaScreenSpaceRects[];
+CC_EXPORT extern const char kUIShowReplicaScreenSpaceRects[];
+CC_EXPORT extern const char kShowOccludingRects[];
+CC_EXPORT extern const char kUIShowOccludingRects[];
+CC_EXPORT extern const char kShowNonOccludingRects[];
+CC_EXPORT extern const char kUIShowNonOccludingRects[];
+
+// Unit test related.
+CC_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
+CC_EXPORT extern const char kCCRebaselinePixeltests[];
+
+}  // namespace switches
+}  // namespace cc
+
+#endif  // CC_BASE_SWITCHES_H_
diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc
new file mode 100644
index 0000000..879d421
--- /dev/null
+++ b/cc/base/tiling_data.cc
@@ -0,0 +1,668 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/tiling_data.h"
+
+#include <algorithm>
+
+#include "ui/gfx/rect.h"
+#include "ui/gfx/vector2d.h"
+
+namespace cc {
+
+static int ComputeNumTiles(int max_texture_size,
+                           int total_size,
+                           int border_texels) {
+  if (max_texture_size - 2 * border_texels <= 0)
+    return total_size > 0 && max_texture_size >= total_size ? 1 : 0;
+
+  int num_tiles = std::max(1,
+                           1 + (total_size - 1 - 2 * border_texels) /
+                           (max_texture_size - 2 * border_texels));
+  return total_size > 0 ? num_tiles : 0;
+}
+
+TilingData::TilingData()
+    : border_texels_(0) {
+  RecomputeNumTiles();
+}
+
+TilingData::TilingData(const gfx::Size& max_texture_size,
+                       const gfx::Size& tiling_size,
+                       bool has_border_texels)
+    : max_texture_size_(max_texture_size),
+      tiling_size_(tiling_size),
+      border_texels_(has_border_texels ? 1 : 0) {
+  RecomputeNumTiles();
+}
+
+TilingData::TilingData(const gfx::Size& max_texture_size,
+                       const gfx::Size& tiling_size,
+                       int border_texels)
+    : max_texture_size_(max_texture_size),
+      tiling_size_(tiling_size),
+      border_texels_(border_texels) {
+  RecomputeNumTiles();
+}
+
+void TilingData::SetTilingSize(const gfx::Size& tiling_size) {
+  tiling_size_ = tiling_size;
+  RecomputeNumTiles();
+}
+
+void TilingData::SetMaxTextureSize(const gfx::Size& max_texture_size) {
+  max_texture_size_ = max_texture_size;
+  RecomputeNumTiles();
+}
+
+void TilingData::SetHasBorderTexels(bool has_border_texels) {
+  border_texels_ = has_border_texels ? 1 : 0;
+  RecomputeNumTiles();
+}
+
+void TilingData::SetBorderTexels(int border_texels) {
+  border_texels_ = border_texels;
+  RecomputeNumTiles();
+}
+
+int TilingData::TileXIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_x_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+  int x = (src_position - border_texels_) /
+      (max_texture_size_.width() - 2 * border_texels_);
+  return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::TileYIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_y_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+  int y = (src_position - border_texels_) /
+      (max_texture_size_.height() - 2 * border_texels_);
+  return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_x_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+  int inner_tile_size = max_texture_size_.width() - 2 * border_texels_;
+  int x = (src_position - 2 * border_texels_) / inner_tile_size;
+  return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_y_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+  int inner_tile_size = max_texture_size_.height() - 2 * border_texels_;
+  int y = (src_position - 2 * border_texels_) / inner_tile_size;
+  return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_x_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+  int inner_tile_size = max_texture_size_.width() - 2 * border_texels_;
+  int x = src_position / inner_tile_size;
+  return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const {
+  if (num_tiles_y_ <= 1)
+    return 0;
+
+  DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+  int inner_tile_size = max_texture_size_.height() - 2 * border_texels_;
+  int y = src_position / inner_tile_size;
+  return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBounds(
+    const gfx::Rect& rect) const {
+  if (rect.IsEmpty() || has_empty_bounds())
+    return gfx::Rect();
+  if (rect.x() > tiling_size_.width() || rect.y() > tiling_size_.height())
+    return gfx::Rect();
+  int index_x = TileXIndexFromSrcCoord(rect.x());
+  int index_y = TileYIndexFromSrcCoord(rect.y());
+  int index_right = TileXIndexFromSrcCoord(rect.right() - 1);
+  int index_bottom = TileYIndexFromSrcCoord(rect.bottom() - 1);
+
+  gfx::Rect rect_top_left(TileBounds(index_x, index_y));
+  gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom));
+
+  return gfx::UnionRects(rect_top_left, rect_bottom_right);
+}
+
+gfx::Rect TilingData::ExpandRectToTileBounds(const gfx::Rect& rect) const {
+  if (rect.IsEmpty() || has_empty_bounds())
+    return gfx::Rect();
+  if (rect.x() > tiling_size_.width() || rect.y() > tiling_size_.height())
+    return gfx::Rect();
+  int index_x = FirstBorderTileXIndexFromSrcCoord(rect.x());
+  int index_y = FirstBorderTileYIndexFromSrcCoord(rect.y());
+  int index_right = LastBorderTileXIndexFromSrcCoord(rect.right() - 1);
+  int index_bottom = LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1);
+
+  gfx::Rect rect_top_left(TileBounds(index_x, index_y));
+  gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom));
+
+  return gfx::UnionRects(rect_top_left, rect_bottom_right);
+}
+
+gfx::Rect TilingData::TileBounds(int i, int j) const {
+  AssertTile(i, j);
+  int max_texture_size_x = max_texture_size_.width() - 2 * border_texels_;
+  int max_texture_size_y = max_texture_size_.height() - 2 * border_texels_;
+
+  int lo_x = max_texture_size_x * i;
+  if (i != 0)
+    lo_x += border_texels_;
+
+  int lo_y = max_texture_size_y * j;
+  if (j != 0)
+    lo_y += border_texels_;
+
+  int hi_x = max_texture_size_x * (i + 1) + border_texels_;
+  if (i + 1 == num_tiles_x_)
+    hi_x += border_texels_;
+
+  int hi_y = max_texture_size_y * (j + 1) + border_texels_;
+  if (j + 1 == num_tiles_y_)
+    hi_y += border_texels_;
+
+  hi_x = std::min(hi_x, tiling_size_.width());
+  hi_y = std::min(hi_y, tiling_size_.height());
+
+  int x = lo_x;
+  int y = lo_y;
+  int width = hi_x - lo_x;
+  int height = hi_y - lo_y;
+  DCHECK_GE(x, 0);
+  DCHECK_GE(y, 0);
+  DCHECK_GE(width, 0);
+  DCHECK_GE(height, 0);
+  DCHECK_LE(x, tiling_size_.width());
+  DCHECK_LE(y, tiling_size_.height());
+  return gfx::Rect(x, y, width, height);
+}
+
+gfx::Rect TilingData::TileBoundsWithBorder(int i, int j) const {
+  AssertTile(i, j);
+  int max_texture_size_x = max_texture_size_.width() - 2 * border_texels_;
+  int max_texture_size_y = max_texture_size_.height() - 2 * border_texels_;
+
+  int lo_x = max_texture_size_x * i;
+  int lo_y = max_texture_size_y * j;
+
+  int hi_x = lo_x + max_texture_size_x + 2 * border_texels_;
+  int hi_y = lo_y + max_texture_size_y + 2 * border_texels_;
+
+  hi_x = std::min(hi_x, tiling_size_.width());
+  hi_y = std::min(hi_y, tiling_size_.height());
+
+  int x = lo_x;
+  int y = lo_y;
+  int width = hi_x - lo_x;
+  int height = hi_y - lo_y;
+  DCHECK_GE(x, 0);
+  DCHECK_GE(y, 0);
+  DCHECK_GE(width, 0);
+  DCHECK_GE(height, 0);
+  DCHECK_LE(x, tiling_size_.width());
+  DCHECK_LE(y, tiling_size_.height());
+  return gfx::Rect(x, y, width, height);
+}
+
+int TilingData::TilePositionX(int x_index) const {
+  DCHECK_GE(x_index, 0);
+  DCHECK_LT(x_index, num_tiles_x_);
+
+  int pos = (max_texture_size_.width() - 2 * border_texels_) * x_index;
+  if (x_index != 0)
+    pos += border_texels_;
+
+  return pos;
+}
+
+int TilingData::TilePositionY(int y_index) const {
+  DCHECK_GE(y_index, 0);
+  DCHECK_LT(y_index, num_tiles_y_);
+
+  int pos = (max_texture_size_.height() - 2 * border_texels_) * y_index;
+  if (y_index != 0)
+    pos += border_texels_;
+
+  return pos;
+}
+
+int TilingData::TileSizeX(int x_index) const {
+  DCHECK_GE(x_index, 0);
+  DCHECK_LT(x_index, num_tiles_x_);
+
+  if (!x_index && num_tiles_x_ == 1)
+    return tiling_size_.width();
+  if (!x_index && num_tiles_x_ > 1)
+    return max_texture_size_.width() - border_texels_;
+  if (x_index < num_tiles_x_ - 1)
+    return max_texture_size_.width() - 2 * border_texels_;
+  if (x_index == num_tiles_x_ - 1)
+    return tiling_size_.width() - TilePositionX(x_index);
+
+  NOTREACHED();
+  return 0;
+}
+
+int TilingData::TileSizeY(int y_index) const {
+  DCHECK_GE(y_index, 0);
+  DCHECK_LT(y_index, num_tiles_y_);
+
+  if (!y_index && num_tiles_y_ == 1)
+    return tiling_size_.height();
+  if (!y_index && num_tiles_y_ > 1)
+    return max_texture_size_.height() - border_texels_;
+  if (y_index < num_tiles_y_ - 1)
+    return max_texture_size_.height() - 2 * border_texels_;
+  if (y_index == num_tiles_y_ - 1)
+    return tiling_size_.height() - TilePositionY(y_index);
+
+  NOTREACHED();
+  return 0;
+}
+
+gfx::Vector2d TilingData::TextureOffset(int x_index, int y_index) const {
+  int left = (!x_index || num_tiles_x_ == 1) ? 0 : border_texels_;
+  int top = (!y_index || num_tiles_y_ == 1) ? 0 : border_texels_;
+
+  return gfx::Vector2d(left, top);
+}
+
+void TilingData::RecomputeNumTiles() {
+  num_tiles_x_ = ComputeNumTiles(
+      max_texture_size_.width(), tiling_size_.width(), border_texels_);
+  num_tiles_y_ = ComputeNumTiles(
+      max_texture_size_.height(), tiling_size_.height(), border_texels_);
+}
+
+TilingData::BaseIterator::BaseIterator(const TilingData* tiling_data)
+    : tiling_data_(tiling_data),
+      index_x_(-1),
+      index_y_(-1) {
+}
+
+TilingData::Iterator::Iterator() : BaseIterator(NULL) { done(); }
+
+TilingData::Iterator::Iterator(const TilingData* tiling_data,
+                               const gfx::Rect& consider_rect,
+                               bool include_borders)
+    : BaseIterator(tiling_data), left_(-1), right_(-1), bottom_(-1) {
+  if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+    done();
+    return;
+  }
+
+  gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+  gfx::Rect rect(consider_rect);
+  rect.Intersect(tiling_bounds_rect);
+
+  gfx::Rect top_left_tile;
+  if (include_borders) {
+    index_x_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(rect.x());
+    index_y_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(rect.y());
+    right_ = tiling_data_->LastBorderTileXIndexFromSrcCoord(rect.right() - 1);
+    bottom_ = tiling_data_->LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1);
+    top_left_tile = tiling_data_->TileBoundsWithBorder(index_x_, index_y_);
+  } else {
+    index_x_ = tiling_data_->TileXIndexFromSrcCoord(rect.x());
+    index_y_ = tiling_data_->TileYIndexFromSrcCoord(rect.y());
+    right_ = tiling_data_->TileXIndexFromSrcCoord(rect.right() - 1);
+    bottom_ = tiling_data_->TileYIndexFromSrcCoord(rect.bottom() - 1);
+    top_left_tile = tiling_data_->TileBounds(index_x_, index_y_);
+  }
+  left_ = index_x_;
+
+  // Index functions always return valid indices, so explicitly check
+  // for non-intersecting rects.
+  if (!top_left_tile.Intersects(rect))
+    done();
+}
+
+TilingData::Iterator& TilingData::Iterator::operator++() {
+  if (!*this)
+    return *this;
+
+  index_x_++;
+  if (index_x_ > right_) {
+    index_x_ = left_;
+    index_y_++;
+    if (index_y_ > bottom_)
+      done();
+  }
+
+  return *this;
+}
+
+TilingData::DifferenceIterator::DifferenceIterator(
+    const TilingData* tiling_data,
+    const gfx::Rect& consider_rect,
+    const gfx::Rect& ignore_rect)
+    : BaseIterator(tiling_data),
+      consider_left_(-1),
+      consider_top_(-1),
+      consider_right_(-1),
+      consider_bottom_(-1),
+      ignore_left_(-1),
+      ignore_top_(-1),
+      ignore_right_(-1),
+      ignore_bottom_(-1) {
+  if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+    done();
+    return;
+  }
+
+  gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+  gfx::Rect consider(consider_rect);
+  gfx::Rect ignore(ignore_rect);
+  consider.Intersect(tiling_bounds_rect);
+  ignore.Intersect(tiling_bounds_rect);
+  if (consider.IsEmpty()) {
+    done();
+    return;
+  }
+
+  consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+  consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+  consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
+  consider_bottom_ =
+      tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
+
+  if (!ignore.IsEmpty()) {
+    ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+    ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+    ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+    ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
+
+    // Clamp ignore indices to consider indices.
+    ignore_left_ = std::max(ignore_left_, consider_left_);
+    ignore_top_ = std::max(ignore_top_, consider_top_);
+    ignore_right_ = std::min(ignore_right_, consider_right_);
+    ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
+  }
+
+  if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
+      ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+    done();
+    return;
+  }
+
+  index_x_ = consider_left_;
+  index_y_ = consider_top_;
+
+  if (in_ignore_rect())
+    ++(*this);
+}
+
+TilingData::DifferenceIterator& TilingData::DifferenceIterator::operator++() {
+  if (!*this)
+    return *this;
+
+  index_x_++;
+  if (in_ignore_rect())
+    index_x_ = ignore_right_ + 1;
+
+  if (index_x_ > consider_right_) {
+    index_x_ = consider_left_;
+    index_y_++;
+
+    if (in_ignore_rect()) {
+      index_x_ = ignore_right_ + 1;
+      // If the ignore rect spans the whole consider rect horizontally, then
+      // ignore_right + 1 will be out of bounds.
+      if (in_ignore_rect() || index_x_ > consider_right_) {
+        index_y_ = ignore_bottom_ + 1;
+        index_x_ = consider_left_;
+      }
+    }
+
+    if (index_y_ > consider_bottom_)
+      done();
+  }
+
+  return *this;
+}
+
+TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator()
+    : BaseIterator(NULL) {
+  done();
+}
+
+TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator(
+    const TilingData* tiling_data,
+    const gfx::Rect& consider_rect,
+    const gfx::Rect& ignore_rect,
+    const gfx::Rect& center_rect)
+    : BaseIterator(tiling_data),
+      consider_left_(-1),
+      consider_top_(-1),
+      consider_right_(-1),
+      consider_bottom_(-1),
+      ignore_left_(-1),
+      ignore_top_(-1),
+      ignore_right_(-1),
+      ignore_bottom_(-1),
+      direction_(RIGHT),
+      delta_x_(1),
+      delta_y_(0),
+      current_step_(0),
+      horizontal_step_count_(0),
+      vertical_step_count_(0) {
+  if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+    done();
+    return;
+  }
+
+  gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+  gfx::Rect consider(consider_rect);
+  gfx::Rect ignore(ignore_rect);
+  gfx::Rect center(center_rect);
+  consider.Intersect(tiling_bounds_rect);
+  ignore.Intersect(tiling_bounds_rect);
+  if (consider.IsEmpty()) {
+    done();
+    return;
+  }
+
+  consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+  consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+  consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
+  consider_bottom_ =
+      tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
+
+  if (!ignore.IsEmpty()) {
+    ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+    ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+    ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+    ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
+
+    // Clamp ignore indices to consider indices.
+    ignore_left_ = std::max(ignore_left_, consider_left_);
+    ignore_top_ = std::max(ignore_top_, consider_top_);
+    ignore_right_ = std::min(ignore_right_, consider_right_);
+    ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
+  }
+
+  if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
+      ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+    done();
+    return;
+  }
+
+  // Determine around left, such that it is between -1 and num_tiles_x.
+  int around_left = 0;
+  if (center.x() < 0 || center.IsEmpty())
+    around_left = -1;
+  else if (center.x() > tiling_data->tiling_size().width())
+    around_left = tiling_data->num_tiles_x();
+  else
+    around_left = tiling_data->TileXIndexFromSrcCoord(center.x());
+
+  // Determine around top, such that it is between -1 and num_tiles_y.
+  int around_top = 0;
+  if (center.y() < 0 || center.IsEmpty())
+    around_top = -1;
+  else if (center.y() > tiling_data->tiling_size().height())
+    around_top = tiling_data->num_tiles_y();
+  else
+    around_top = tiling_data->TileYIndexFromSrcCoord(center.y());
+
+  // Determine around right, such that it is between -1 and num_tiles_x.
+  int right_src_coord = center.right() - 1;
+  int around_right = 0;
+  if (right_src_coord < 0 || center.IsEmpty()) {
+    around_right = -1;
+  } else if (right_src_coord > tiling_data->tiling_size().width()) {
+    around_right = tiling_data->num_tiles_x();
+  } else {
+    around_right = tiling_data->TileXIndexFromSrcCoord(right_src_coord);
+  }
+
+  // Determine around bottom, such that it is between -1 and num_tiles_y.
+  int bottom_src_coord = center.bottom() - 1;
+  int around_bottom = 0;
+  if (bottom_src_coord < 0 || center.IsEmpty()) {
+    around_bottom = -1;
+  } else if (bottom_src_coord > tiling_data->tiling_size().height()) {
+    around_bottom = tiling_data->num_tiles_y();
+  } else {
+    around_bottom = tiling_data->TileYIndexFromSrcCoord(bottom_src_coord);
+  }
+
+  vertical_step_count_ = around_bottom - around_top + 1;
+  horizontal_step_count_ = around_right - around_left + 1;
+  current_step_ = horizontal_step_count_ - 1;
+
+  index_x_ = around_right;
+  index_y_ = around_bottom;
+
+  // The current index is the bottom right of the around rect, which is also
+  // ignored. So we have to advance.
+  ++(*this);
+}
+
+TilingData::SpiralDifferenceIterator& TilingData::SpiralDifferenceIterator::
+operator++() {
+  int cannot_hit_consider_count = 0;
+  while (cannot_hit_consider_count < 4) {
+    if (needs_direction_switch())
+      switch_direction();
+
+    index_x_ += delta_x_;
+    index_y_ += delta_y_;
+    ++current_step_;
+
+    if (in_consider_rect()) {
+      cannot_hit_consider_count = 0;
+
+      if (!in_ignore_rect())
+        break;
+
+      // Steps needed to reach the very edge of the ignore rect, while remaining
+      // inside (so that the continue would take us outside).
+      int steps_to_edge = 0;
+      switch (direction_) {
+        case UP:
+          steps_to_edge = index_y_ - ignore_top_;
+          break;
+        case LEFT:
+          steps_to_edge = index_x_ - ignore_left_;
+          break;
+        case DOWN:
+          steps_to_edge = ignore_bottom_ - index_y_;
+          break;
+        case RIGHT:
+          steps_to_edge = ignore_right_ - index_x_;
+          break;
+      }
+
+      // We need to switch directions in |max_steps|.
+      int max_steps = current_step_count() - current_step_;
+
+      int steps_to_take = std::min(steps_to_edge, max_steps);
+      DCHECK_GE(steps_to_take, 0);
+
+      index_x_ += steps_to_take * delta_x_;
+      index_y_ += steps_to_take * delta_y_;
+      current_step_ += steps_to_take;
+    } else {
+      int max_steps = current_step_count() - current_step_;
+      int steps_to_take = max_steps;
+      bool can_hit_consider_rect = false;
+      switch (direction_) {
+        case UP:
+          if (valid_column() && consider_bottom_ < index_y_)
+            steps_to_take = index_y_ - consider_bottom_ - 1;
+          can_hit_consider_rect |= consider_right_ >= index_x_;
+          break;
+        case LEFT:
+          if (valid_row() && consider_right_ < index_x_)
+            steps_to_take = index_x_ - consider_right_ - 1;
+          can_hit_consider_rect |= consider_top_ <= index_y_;
+          break;
+        case DOWN:
+          if (valid_column() && consider_top_ > index_y_)
+            steps_to_take = consider_top_ - index_y_ - 1;
+          can_hit_consider_rect |= consider_left_ <= index_x_;
+          break;
+        case RIGHT:
+          if (valid_row() && consider_left_ > index_x_)
+            steps_to_take = consider_left_ - index_x_ - 1;
+          can_hit_consider_rect |= consider_bottom_ >= index_y_;
+          break;
+      }
+      steps_to_take = std::min(steps_to_take, max_steps);
+      DCHECK_GE(steps_to_take, 0);
+
+      index_x_ += steps_to_take * delta_x_;
+      index_y_ += steps_to_take * delta_y_;
+      current_step_ += steps_to_take;
+
+      if (can_hit_consider_rect)
+        cannot_hit_consider_count = 0;
+      else
+        ++cannot_hit_consider_count;
+    }
+  }
+
+  if (cannot_hit_consider_count >= 4)
+    done();
+  return *this;
+}
+
+bool TilingData::SpiralDifferenceIterator::needs_direction_switch() const {
+  return current_step_ >= current_step_count();
+}
+
+void TilingData::SpiralDifferenceIterator::switch_direction() {
+  int new_delta_x_ = delta_y_;
+  delta_y_ = -delta_x_;
+  delta_x_ = new_delta_x_;
+
+  current_step_ = 0;
+  direction_ = static_cast<Direction>((direction_ + 1) % 4);
+
+  if (direction_ == RIGHT || direction_ == LEFT) {
+    ++vertical_step_count_;
+    ++horizontal_step_count_;
+  }
+}
+
+}  // namespace cc
diff --git a/cc/base/tiling_data.h b/cc/base/tiling_data.h
new file mode 100644
index 0000000..a2e0a13
--- /dev/null
+++ b/cc/base/tiling_data.h
@@ -0,0 +1,206 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_TILING_DATA_H_
+#define CC_BASE_TILING_DATA_H_
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Vector2d;
+}
+
+namespace cc {
+
+class CC_EXPORT TilingData {
+ public:
+  TilingData();
+  TilingData(const gfx::Size& max_texture_size,
+             const gfx::Size& tiling_size,
+             bool has_border_texels);
+  TilingData(const gfx::Size& max_texture_size,
+             const gfx::Size& tiling_size,
+             int border_texels);
+
+  gfx::Size tiling_size() const { return tiling_size_; }
+  void SetTilingSize(const gfx::Size& tiling_size);
+
+  gfx::Size max_texture_size() const { return max_texture_size_; }
+  void SetMaxTextureSize(const gfx::Size& max_texture_size);
+
+  int border_texels() const { return border_texels_; }
+  void SetHasBorderTexels(bool has_border_texels);
+  void SetBorderTexels(int border_texels);
+
+  bool has_empty_bounds() const { return !num_tiles_x_ || !num_tiles_y_; }
+  int num_tiles_x() const { return num_tiles_x_; }
+  int num_tiles_y() const { return num_tiles_y_; }
+  // Return the tile index whose non-border texels include src_position.
+  int TileXIndexFromSrcCoord(int src_position) const;
+  int TileYIndexFromSrcCoord(int src_position) const;
+  // Return the lowest tile index whose border texels include src_position.
+  int FirstBorderTileXIndexFromSrcCoord(int src_position) const;
+  int FirstBorderTileYIndexFromSrcCoord(int src_position) const;
+  // Return the highest tile index whose border texels include src_position.
+  int LastBorderTileXIndexFromSrcCoord(int src_position) const;
+  int LastBorderTileYIndexFromSrcCoord(int src_position) const;
+
+  gfx::Rect ExpandRectIgnoringBordersToTileBounds(const gfx::Rect& rect) const;
+  gfx::Rect ExpandRectToTileBounds(const gfx::Rect& rect) const;
+
+  gfx::Rect TileBounds(int i, int j) const;
+  gfx::Rect TileBoundsWithBorder(int i, int j) const;
+  int TilePositionX(int x_index) const;
+  int TilePositionY(int y_index) const;
+  int TileSizeX(int x_index) const;
+  int TileSizeY(int y_index) const;
+
+  // Difference between TileBound's and TileBoundWithBorder's origin().
+  gfx::Vector2d TextureOffset(int x_index, int y_index) const;
+
+  class CC_EXPORT BaseIterator {
+   public:
+    operator bool() const { return index_x_ != -1 && index_y_ != -1; }
+
+    int index_x() const { return index_x_; }
+    int index_y() const { return index_y_; }
+    std::pair<int, int> index() const {
+     return std::make_pair(index_x_, index_y_);
+    }
+
+   protected:
+    explicit BaseIterator(const TilingData* tiling_data);
+    void done() {
+      index_x_ = -1;
+      index_y_ = -1;
+    }
+
+    const TilingData* tiling_data_;
+    int index_x_;
+    int index_y_;
+  };
+
+  // Iterate through tiles whose bounds + optional border intersect with |rect|.
+  class CC_EXPORT Iterator : public BaseIterator {
+   public:
+    Iterator();
+    Iterator(const TilingData* tiling_data,
+             const gfx::Rect& consider_rect,
+             bool include_borders);
+    Iterator& operator++();
+
+   private:
+    int left_;
+    int right_;
+    int bottom_;
+  };
+
+  // Iterate through all indices whose bounds (not including borders) intersect
+  // with |consider| but which also do not intersect with |ignore|.
+  class CC_EXPORT DifferenceIterator : public BaseIterator {
+   public:
+    DifferenceIterator(const TilingData* tiling_data,
+                       const gfx::Rect& consider_rect,
+                       const gfx::Rect& ignore_rect);
+    DifferenceIterator& operator++();
+
+   private:
+    bool in_ignore_rect() const {
+     return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
+       index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
+    }
+
+    int consider_left_;
+    int consider_top_;
+    int consider_right_;
+    int consider_bottom_;
+    int ignore_left_;
+    int ignore_top_;
+    int ignore_right_;
+    int ignore_bottom_;
+  };
+
+  // Iterate through all indices whose bounds + border intersect with
+  // |consider| but which also do not intersect with |ignore|. The iterator
+  // order is a counterclockwise spiral around the given center.
+  class CC_EXPORT SpiralDifferenceIterator : public BaseIterator {
+   public:
+    SpiralDifferenceIterator();
+    SpiralDifferenceIterator(const TilingData* tiling_data,
+                             const gfx::Rect& consider_rect,
+                             const gfx::Rect& ignore_rect,
+                             const gfx::Rect& center_rect);
+    SpiralDifferenceIterator& operator++();
+
+   private:
+    bool in_consider_rect() const {
+      return index_x_ >= consider_left_ && index_x_ <= consider_right_ &&
+             index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
+    }
+    bool in_ignore_rect() const {
+      return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
+             index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
+    }
+    bool valid_column() const {
+      return index_x_ >= consider_left_ && index_x_ <= consider_right_;
+    }
+    bool valid_row() const {
+      return index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
+    }
+
+    int current_step_count() const {
+      return (direction_ == UP || direction_ == DOWN) ? vertical_step_count_
+                                                      : horizontal_step_count_;
+    }
+
+    bool needs_direction_switch() const;
+    void switch_direction();
+
+    int consider_left_;
+    int consider_top_;
+    int consider_right_;
+    int consider_bottom_;
+    int ignore_left_;
+    int ignore_top_;
+    int ignore_right_;
+    int ignore_bottom_;
+
+    enum Direction { UP, LEFT, DOWN, RIGHT };
+
+    Direction direction_;
+    int delta_x_;
+    int delta_y_;
+    int current_step_;
+    int horizontal_step_count_;
+    int vertical_step_count_;
+  };
+
+ private:
+  void AssertTile(int i, int j) const {
+    DCHECK_GE(i,  0);
+    DCHECK_LT(i, num_tiles_x_);
+    DCHECK_GE(j, 0);
+    DCHECK_LT(j, num_tiles_y_);
+  }
+
+  void RecomputeNumTiles();
+
+  gfx::Size max_texture_size_;
+  gfx::Size tiling_size_;
+  int border_texels_;
+
+  // These are computed values.
+  int num_tiles_x_;
+  int num_tiles_y_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_TILING_DATA_H_
diff --git a/cc/base/tiling_data_unittest.cc b/cc/base/tiling_data_unittest.cc
new file mode 100644
index 0000000..9c2f944
--- /dev/null
+++ b/cc/base/tiling_data_unittest.cc
@@ -0,0 +1,1940 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/tiling_data.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "cc/test/geometry_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+int NumTiles(const gfx::Size& max_texture_size,
+             const gfx::Size& tiling_size,
+             bool has_border_texels) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  int num_tiles = tiling.num_tiles_x() * tiling.num_tiles_y();
+
+  // Assert no overflow.
+  EXPECT_GE(num_tiles, 0);
+  if (num_tiles > 0)
+    EXPECT_EQ(num_tiles / tiling.num_tiles_x(), tiling.num_tiles_y());
+
+  return num_tiles;
+}
+
+int XIndex(const gfx::Size& max_texture_size,
+           const gfx::Size& tiling_size,
+           bool has_border_texels,
+           int x_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TileXIndexFromSrcCoord(x_coord);
+}
+
+int YIndex(const gfx::Size& max_texture_size,
+           const gfx::Size& tiling_size,
+           bool has_border_texels,
+           int y_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TileYIndexFromSrcCoord(y_coord);
+}
+
+int MinBorderXIndex(const gfx::Size& max_texture_size,
+                    const gfx::Size& tiling_size,
+                    bool has_border_texels,
+                    int x_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.FirstBorderTileXIndexFromSrcCoord(x_coord);
+}
+
+int MinBorderYIndex(const gfx::Size& max_texture_size,
+                    const gfx::Size& tiling_size,
+                    bool has_border_texels,
+                    int y_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.FirstBorderTileYIndexFromSrcCoord(y_coord);
+}
+
+int MaxBorderXIndex(const gfx::Size& max_texture_size,
+                    const gfx::Size& tiling_size,
+                    bool has_border_texels,
+                    int x_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.LastBorderTileXIndexFromSrcCoord(x_coord);
+}
+
+int MaxBorderYIndex(const gfx::Size& max_texture_size,
+                    const gfx::Size& tiling_size,
+                    bool has_border_texels,
+                    int y_coord) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.LastBorderTileYIndexFromSrcCoord(y_coord);
+}
+
+int PosX(const gfx::Size& max_texture_size,
+         const gfx::Size& tiling_size,
+         bool has_border_texels,
+         int x_index) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TilePositionX(x_index);
+}
+
+int PosY(const gfx::Size& max_texture_size,
+         const gfx::Size& tiling_size,
+         bool has_border_texels,
+         int y_index) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TilePositionY(y_index);
+}
+
+int SizeX(const gfx::Size& max_texture_size,
+          const gfx::Size& tiling_size,
+          bool has_border_texels,
+          int x_index) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TileSizeX(x_index);
+}
+
+int SizeY(const gfx::Size& max_texture_size,
+          const gfx::Size& tiling_size,
+          bool has_border_texels,
+          int y_index) {
+  TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+  return tiling.TileSizeY(y_index);
+}
+
+class TilingDataTest : public ::testing::TestWithParam<gfx::Point> {};
+
+TEST(TilingDataTest, NumTiles_NoTiling) {
+  EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(15, 15), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(1, 16), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(15, 15), gfx::Size(15, 15), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(32, 16), gfx::Size(32, 16), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(32, 16), gfx::Size(32, 16), true));
+}
+
+TEST(TilingDataTest, NumTiles_TilingNoBorders) {
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 0), false));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(4, 0), false));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 4), false));
+  EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(4, 0), false));
+  EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(0, 4), false));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(1, 1), false));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(1, 1), gfx::Size(1, 1), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(1, 1), gfx::Size(1, 2), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(1, 1), gfx::Size(2, 1), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 1), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 2), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 1), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 2), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(3, 3), false));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(1, 4), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(2, 4), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(3, 4), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(4, 4), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(5, 4), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(6, 4), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(7, 4), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(8, 4), false));
+  EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(9, 4), false));
+  EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(10, 4), false));
+  EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(11, 4), false));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(1, 5), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(2, 5), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(3, 5), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(4, 5), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(5, 5), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(6, 5), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(7, 5), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(8, 5), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(9, 5), false));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(10, 5), false));
+  EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(11, 5), false));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), false));
+  EXPECT_EQ(1, NumTiles(gfx::Size(17, 17), gfx::Size(16, 16), false));
+  EXPECT_EQ(4, NumTiles(gfx::Size(15, 15), gfx::Size(16, 16), false));
+  EXPECT_EQ(4, NumTiles(gfx::Size(8, 8), gfx::Size(16, 16), false));
+  EXPECT_EQ(6, NumTiles(gfx::Size(8, 8), gfx::Size(17, 16), false));
+
+  EXPECT_EQ(8, NumTiles(gfx::Size(5, 8), gfx::Size(17, 16), false));
+}
+
+TEST(TilingDataTest, NumTiles_TilingWithBorders) {
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 0), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(4, 0), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 4), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(4, 0), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(0, 4), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(1, 1), true));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(1, 1), gfx::Size(1, 1), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(1, 1), gfx::Size(1, 2), true));
+  EXPECT_EQ(0, NumTiles(gfx::Size(1, 1), gfx::Size(2, 1), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 1), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 2), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 1), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 2), true));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(1, 3), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(2, 3), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(3, 3), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(4, 3), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(3, 3), gfx::Size(5, 3), true));
+  EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(6, 3), true));
+  EXPECT_EQ(5, NumTiles(gfx::Size(3, 3), gfx::Size(7, 3), true));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(1, 4), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(2, 4), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(3, 4), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(4, 4), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(5, 4), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(6, 4), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(7, 4), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(8, 4), true));
+  EXPECT_EQ(4, NumTiles(gfx::Size(4, 4), gfx::Size(9, 4), true));
+  EXPECT_EQ(4, NumTiles(gfx::Size(4, 4), gfx::Size(10, 4), true));
+  EXPECT_EQ(5, NumTiles(gfx::Size(4, 4), gfx::Size(11, 4), true));
+
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(1, 5), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(2, 5), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(3, 5), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(4, 5), true));
+  EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(5, 5), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(6, 5), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(7, 5), true));
+  EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(8, 5), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(9, 5), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(10, 5), true));
+  EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(11, 5), true));
+
+  EXPECT_EQ(30, NumTiles(gfx::Size(8, 5), gfx::Size(16, 32), true));
+}
+
+TEST(TilingDataTest, TileXIndexFromSrcCoord) {
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(4, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(5, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(6, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, XIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+  EXPECT_EQ(0, XIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+  EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+  EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, FirstBorderTileXIndexFromSrcCoord) {
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(4, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(5, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(6, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+  EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+  EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, LastBorderTileXIndexFromSrcCoord) {
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(4, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(5, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(6, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+  EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, TileYIndexFromSrcCoord) {
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(4, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(5, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(6, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, YIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+  EXPECT_EQ(0, YIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+  EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+  EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, FirstBorderTileYIndexFromSrcCoord) {
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(4, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(5, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(6, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+  EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+  EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, LastBorderTileYIndexFromSrcCoord) {
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+  EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+  EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+  EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+  EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+  EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+  EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+  EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+  EXPECT_EQ(4, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+  EXPECT_EQ(5, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+  EXPECT_EQ(6, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+  EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+  EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+  EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+  EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+  EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+  EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+  EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, TileSizeX) {
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(5, 5), false, 0));
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(5, 5), true, 0));
+
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), false, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), false, 1));
+  EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), true, 0));
+  EXPECT_EQ(2, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), true, 1));
+
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), false, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), false, 1));
+  EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), true, 0));
+  EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), true, 1));
+
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 2));
+
+  EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(11, 11), true, 2));
+  EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(12, 12), true, 2));
+
+  EXPECT_EQ(3, SizeX(gfx::Size(5, 9), gfx::Size(12, 17), true, 2));
+}
+
+TEST(TilingDataTest, TileSizeY) {
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(5, 5), false, 0));
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(5, 5), true, 0));
+
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), false, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), false, 1));
+  EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), true, 0));
+  EXPECT_EQ(2, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), true, 1));
+
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), false, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), false, 1));
+  EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), true, 0));
+  EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), true, 1));
+
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), false, 0));
+  EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), false, 1));
+  EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 1));
+  EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 2));
+
+  EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(11, 11), true, 2));
+  EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(12, 12), true, 2));
+
+  EXPECT_EQ(3, SizeY(gfx::Size(9, 5), gfx::Size(17, 12), true, 2));
+}
+
+TEST(TilingDataTest, TileSizeX_and_TilePositionX) {
+  // Single tile cases:
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 100), false, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 100), false, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 1), false, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 1), false, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 100), false, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 100), false, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 100), true, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 100), true, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 1), true, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 1), true, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 100), true, 0));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 100), true, 0));
+
+  // Multiple tiles:
+  // no border
+  // positions 0, 3
+  EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(6, 1), false));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), false, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), false, 1));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 1), false, 0));
+  EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 1), false, 1));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 100), false, 0));
+  EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 100), false, 1));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 100), false, 0));
+  EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 100), false, 1));
+
+  // Multiple tiles:
+  // with border
+  // positions 0, 2, 3, 4
+  EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(6, 1), true));
+  EXPECT_EQ(2, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 1));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 2));
+  EXPECT_EQ(2, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 3));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 0));
+  EXPECT_EQ(2, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 1));
+  EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 2));
+  EXPECT_EQ(4, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 3));
+  EXPECT_EQ(2, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 0));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 1));
+  EXPECT_EQ(1, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 2));
+  EXPECT_EQ(2, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 3));
+  EXPECT_EQ(0, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 0));
+  EXPECT_EQ(2, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 1));
+  EXPECT_EQ(3, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 2));
+  EXPECT_EQ(4, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 3));
+}
+
+TEST(TilingDataTest, TileSizeY_and_TilePositionY) {
+  // Single tile cases:
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(100, 1), false, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 1), false, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 3), false, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 3), false, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 3), false, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 3), false, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(100, 1), true, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 1), true, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 3), true, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 3), true, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 3), true, 0));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 3), true, 0));
+
+  // Multiple tiles:
+  // no border
+  // positions 0, 3
+  EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(1, 6), false));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), false, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), false, 1));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 6), false, 0));
+  EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(1, 6), false, 1));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 6), false, 0));
+  EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 6), false, 1));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 6), false, 0));
+  EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(100, 6), false, 1));
+
+  // Multiple tiles:
+  // with border
+  // positions 0, 2, 3, 4
+  EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(1, 6), true));
+  EXPECT_EQ(2, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 1));
+  EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 2));
+  EXPECT_EQ(2, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 3));
+  EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 0));
+  EXPECT_EQ(2, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 1));
+  EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 2));
+  EXPECT_EQ(4, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 3));
+  EXPECT_EQ(2, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 0));
+  EXPECT_EQ(1, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 1));
+  EXPECT_EQ(1, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 2));
+  EXPECT_EQ(2, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 3));
+  EXPECT_EQ(0, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 0));
+  EXPECT_EQ(2, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 1));
+  EXPECT_EQ(3, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 2));
+  EXPECT_EQ(4, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 3));
+}
+
+TEST(TilingDataTest, SetTotalSize) {
+  TilingData data(gfx::Size(5, 5), gfx::Size(5, 5), false);
+  EXPECT_EQ(5, data.tiling_size().width());
+  EXPECT_EQ(5, data.tiling_size().height());
+  EXPECT_EQ(1, data.num_tiles_x());
+  EXPECT_EQ(5, data.TileSizeX(0));
+  EXPECT_EQ(1, data.num_tiles_y());
+  EXPECT_EQ(5, data.TileSizeY(0));
+
+  data.SetTilingSize(gfx::Size(6, 5));
+  EXPECT_EQ(6, data.tiling_size().width());
+  EXPECT_EQ(5, data.tiling_size().height());
+  EXPECT_EQ(2, data.num_tiles_x());
+  EXPECT_EQ(5, data.TileSizeX(0));
+  EXPECT_EQ(1, data.TileSizeX(1));
+  EXPECT_EQ(1, data.num_tiles_y());
+  EXPECT_EQ(5, data.TileSizeY(0));
+
+  data.SetTilingSize(gfx::Size(5, 12));
+  EXPECT_EQ(5, data.tiling_size().width());
+  EXPECT_EQ(12, data.tiling_size().height());
+  EXPECT_EQ(1, data.num_tiles_x());
+  EXPECT_EQ(5, data.TileSizeX(0));
+  EXPECT_EQ(3, data.num_tiles_y());
+  EXPECT_EQ(5, data.TileSizeY(0));
+  EXPECT_EQ(5, data.TileSizeY(1));
+  EXPECT_EQ(2, data.TileSizeY(2));
+}
+
+TEST(TilingDataTest, SetMaxTextureSizeNoBorders) {
+  TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), false);
+  EXPECT_EQ(2, data.num_tiles_x());
+  EXPECT_EQ(4, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(32, 32));
+  EXPECT_EQ(gfx::Size(32, 32), data.max_texture_size());
+  EXPECT_EQ(1, data.num_tiles_x());
+  EXPECT_EQ(1, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(2, 2));
+  EXPECT_EQ(gfx::Size(2, 2), data.max_texture_size());
+  EXPECT_EQ(8, data.num_tiles_x());
+  EXPECT_EQ(16, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(5, 5));
+  EXPECT_EQ(gfx::Size(5, 5), data.max_texture_size());
+  EXPECT_EQ(4, data.num_tiles_x());
+  EXPECT_EQ(7, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(8, 5));
+  EXPECT_EQ(gfx::Size(8, 5), data.max_texture_size());
+  EXPECT_EQ(2, data.num_tiles_x());
+  EXPECT_EQ(7, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, SetMaxTextureSizeBorders) {
+  TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), true);
+  EXPECT_EQ(3, data.num_tiles_x());
+  EXPECT_EQ(5, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(32, 32));
+  EXPECT_EQ(gfx::Size(32, 32), data.max_texture_size());
+  EXPECT_EQ(1, data.num_tiles_x());
+  EXPECT_EQ(1, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(2, 2));
+  EXPECT_EQ(gfx::Size(2, 2), data.max_texture_size());
+  EXPECT_EQ(0, data.num_tiles_x());
+  EXPECT_EQ(0, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(5, 5));
+  EXPECT_EQ(gfx::Size(5, 5), data.max_texture_size());
+  EXPECT_EQ(5, data.num_tiles_x());
+  EXPECT_EQ(10, data.num_tiles_y());
+
+  data.SetMaxTextureSize(gfx::Size(8, 5));
+  EXPECT_EQ(gfx::Size(8, 5), data.max_texture_size());
+  EXPECT_EQ(3, data.num_tiles_x());
+  EXPECT_EQ(10, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsEmpty) {
+  TilingData empty_total_size(gfx::Size(0, 0), gfx::Size(8, 8), true);
+  EXPECT_RECT_EQ(
+      gfx::Rect(),
+      empty_total_size.ExpandRectIgnoringBordersToTileBounds(gfx::Rect()));
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect(100, 100, 100, 100)));
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect(100, 100)));
+
+  TilingData empty_max_texture_size(gfx::Size(8, 8), gfx::Size(0, 0), true);
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect()));
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect(100, 100, 100, 100)));
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect(100, 100)));
+}
+
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBounds) {
+  TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true);
+
+  // Small rect at origin rounds up to tile 0, 0.
+  gfx::Rect at_origin_src(1, 1);
+  gfx::Rect at_origin_result(data.TileBounds(0, 0));
+  EXPECT_NE(at_origin_src, at_origin_result);
+  EXPECT_RECT_EQ(at_origin_result,
+                 data.ExpandRectIgnoringBordersToTileBounds(at_origin_src));
+
+  // Arbitrary internal rect.
+  gfx::Rect rect_src(6, 6, 1, 3);
+  // Tile 2, 2 => gfx::Rect(4, 4, 4, 4)
+  // Tile 2, 3 => gfx::Rect(4, 6, 4, 4)
+  gfx::Rect rect_result(
+      gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(2, 3)));
+  EXPECT_NE(rect_src, rect_result);
+  EXPECT_RECT_EQ(rect_result,
+                 data.ExpandRectIgnoringBordersToTileBounds(rect_src));
+
+  // On tile bounds does not round up to next tile (ignores the border).
+  gfx::Rect border_rect_src(
+      gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+  gfx::Rect border_rect_result(
+      gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+  EXPECT_RECT_EQ(border_rect_result,
+                 data.ExpandRectIgnoringBordersToTileBounds(border_rect_src));
+
+  // Equal to tiling rect.
+  EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+                 data.ExpandRectIgnoringBordersToTileBounds(
+                     gfx::Rect(data.tiling_size())));
+
+  // Containing, but larger than tiling rect.
+  EXPECT_RECT_EQ(
+      gfx::Rect(data.tiling_size()),
+      data.ExpandRectIgnoringBordersToTileBounds(gfx::Rect(100, 100)));
+
+  // Non-intersecting with tiling rect.
+  gfx::Rect non_intersect(200, 200, 100, 100);
+  EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size())));
+  EXPECT_RECT_EQ(gfx::Rect(),
+                 data.ExpandRectIgnoringBordersToTileBounds(non_intersect));
+
+  TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true);
+
+  // Inside other tile border texels doesn't include other tiles.
+  gfx::Rect inner_rect_src(data2.TileBounds(1, 1));
+  inner_rect_src.Inset(data2.border_texels(), data.border_texels());
+  gfx::Rect inner_rect_result(data2.TileBounds(1, 1));
+  gfx::Rect expanded =
+      data2.ExpandRectIgnoringBordersToTileBounds(inner_rect_src);
+  EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString());
+}
+
+TEST(TilingDataTest, ExpandRectToTileBounds) {
+  TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true);
+
+  // Small rect at origin rounds up to tile 0, 0.
+  gfx::Rect at_origin_src(1, 1);
+  gfx::Rect at_origin_result(data.TileBounds(0, 0));
+  EXPECT_NE(at_origin_src, at_origin_result);
+  EXPECT_RECT_EQ(at_origin_result, data.ExpandRectToTileBounds(at_origin_src));
+
+  // Arbitrary internal rect.
+  gfx::Rect rect_src(6, 6, 1, 3);
+  // Tile 2, 2 => gfx::Rect(4, 4, 4, 4)
+  // Tile 3, 4 => gfx::Rect(6, 8, 4, 4)
+  gfx::Rect rect_result(
+      gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(3, 4)));
+  EXPECT_NE(rect_src, rect_result);
+  EXPECT_RECT_EQ(rect_result, data.ExpandRectToTileBounds(rect_src));
+
+  // On tile bounds rounds up to next tile (since border overlaps).
+  gfx::Rect border_rect_src(
+      gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+  gfx::Rect border_rect_result(
+      gfx::UnionRects(data.TileBounds(0, 1), data.TileBounds(4, 5)));
+  EXPECT_RECT_EQ(border_rect_result,
+                 data.ExpandRectToTileBounds(border_rect_src));
+
+  // Equal to tiling rect.
+  EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+                 data.ExpandRectToTileBounds(gfx::Rect(data.tiling_size())));
+
+  // Containing, but larger than tiling rect.
+  EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+                 data.ExpandRectToTileBounds(gfx::Rect(100, 100)));
+
+  // Non-intersecting with tiling rect.
+  gfx::Rect non_intersect(200, 200, 100, 100);
+  EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size())));
+  EXPECT_RECT_EQ(gfx::Rect(), data.ExpandRectToTileBounds(non_intersect));
+
+  TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true);
+
+  // Inside other tile border texels doesn't include other tiles.
+  gfx::Rect inner_rect_src(data2.TileBounds(1, 1));
+  inner_rect_src.Inset(data2.border_texels(), data.border_texels());
+  gfx::Rect inner_rect_result(data2.TileBounds(1, 1));
+  gfx::Rect expanded = data2.ExpandRectToTileBounds(inner_rect_src);
+  EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString());
+}
+
+TEST(TilingDataTest, Assignment) {
+  {
+    TilingData source(gfx::Size(8, 8), gfx::Size(16, 32), true);
+    TilingData dest = source;
+    EXPECT_EQ(source.border_texels(), dest.border_texels());
+    EXPECT_EQ(source.max_texture_size(), dest.max_texture_size());
+    EXPECT_EQ(source.num_tiles_x(), dest.num_tiles_x());
+    EXPECT_EQ(source.num_tiles_y(), dest.num_tiles_y());
+    EXPECT_EQ(source.tiling_size(), dest.tiling_size());
+  }
+  {
+    TilingData source(gfx::Size(7, 3), gfx::Size(6, 100), false);
+    TilingData dest(source);
+    EXPECT_EQ(source.border_texels(), dest.border_texels());
+    EXPECT_EQ(source.max_texture_size(), dest.max_texture_size());
+    EXPECT_EQ(source.num_tiles_x(), dest.num_tiles_x());
+    EXPECT_EQ(source.num_tiles_y(), dest.num_tiles_y());
+    EXPECT_EQ(source.tiling_size(), dest.tiling_size());
+  }
+}
+
+TEST(TilingDataTest, SetBorderTexels) {
+  TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), false);
+  EXPECT_EQ(2, data.num_tiles_x());
+  EXPECT_EQ(4, data.num_tiles_y());
+
+  data.SetHasBorderTexels(true);
+  EXPECT_EQ(3, data.num_tiles_x());
+  EXPECT_EQ(5, data.num_tiles_y());
+
+  data.SetHasBorderTexels(false);
+  EXPECT_EQ(2, data.num_tiles_x());
+  EXPECT_EQ(4, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, LargeBorders) {
+  TilingData data(gfx::Size(100, 80), gfx::Size(200, 145), 30);
+  EXPECT_EQ(30, data.border_texels());
+
+  EXPECT_EQ(70, data.TileSizeX(0));
+  EXPECT_EQ(40, data.TileSizeX(1));
+  EXPECT_EQ(40, data.TileSizeX(2));
+  EXPECT_EQ(50, data.TileSizeX(3));
+  EXPECT_EQ(4, data.num_tiles_x());
+
+  EXPECT_EQ(50, data.TileSizeY(0));
+  EXPECT_EQ(20, data.TileSizeY(1));
+  EXPECT_EQ(20, data.TileSizeY(2));
+  EXPECT_EQ(20, data.TileSizeY(3));
+  EXPECT_EQ(35, data.TileSizeY(4));
+  EXPECT_EQ(5, data.num_tiles_y());
+
+  EXPECT_RECT_EQ(gfx::Rect(70, 50), data.TileBounds(0, 0));
+  EXPECT_RECT_EQ(gfx::Rect(70, 50, 40, 20), data.TileBounds(1, 1));
+  EXPECT_RECT_EQ(gfx::Rect(110, 110, 40, 35), data.TileBounds(2, 4));
+  EXPECT_RECT_EQ(gfx::Rect(150, 70, 50, 20), data.TileBounds(3, 2));
+  EXPECT_RECT_EQ(gfx::Rect(150, 110, 50, 35), data.TileBounds(3, 4));
+
+  EXPECT_RECT_EQ(gfx::Rect(100, 80), data.TileBoundsWithBorder(0, 0));
+  EXPECT_RECT_EQ(gfx::Rect(40, 20, 100, 80), data.TileBoundsWithBorder(1, 1));
+  EXPECT_RECT_EQ(gfx::Rect(80, 80, 100, 65), data.TileBoundsWithBorder(2, 4));
+  EXPECT_RECT_EQ(gfx::Rect(120, 40, 80, 80), data.TileBoundsWithBorder(3, 2));
+  EXPECT_RECT_EQ(gfx::Rect(120, 80, 80, 65), data.TileBoundsWithBorder(3, 4));
+
+  EXPECT_EQ(0, data.TileXIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.TileXIndexFromSrcCoord(69));
+  EXPECT_EQ(1, data.TileXIndexFromSrcCoord(70));
+  EXPECT_EQ(1, data.TileXIndexFromSrcCoord(109));
+  EXPECT_EQ(2, data.TileXIndexFromSrcCoord(110));
+  EXPECT_EQ(2, data.TileXIndexFromSrcCoord(149));
+  EXPECT_EQ(3, data.TileXIndexFromSrcCoord(150));
+  EXPECT_EQ(3, data.TileXIndexFromSrcCoord(199));
+
+  EXPECT_EQ(0, data.TileYIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.TileYIndexFromSrcCoord(49));
+  EXPECT_EQ(1, data.TileYIndexFromSrcCoord(50));
+  EXPECT_EQ(1, data.TileYIndexFromSrcCoord(69));
+  EXPECT_EQ(2, data.TileYIndexFromSrcCoord(70));
+  EXPECT_EQ(2, data.TileYIndexFromSrcCoord(89));
+  EXPECT_EQ(3, data.TileYIndexFromSrcCoord(90));
+  EXPECT_EQ(3, data.TileYIndexFromSrcCoord(109));
+  EXPECT_EQ(4, data.TileYIndexFromSrcCoord(110));
+  EXPECT_EQ(4, data.TileYIndexFromSrcCoord(144));
+
+  EXPECT_EQ(0, data.FirstBorderTileXIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.FirstBorderTileXIndexFromSrcCoord(99));
+  EXPECT_EQ(1, data.FirstBorderTileXIndexFromSrcCoord(100));
+  EXPECT_EQ(1, data.FirstBorderTileXIndexFromSrcCoord(139));
+  EXPECT_EQ(2, data.FirstBorderTileXIndexFromSrcCoord(140));
+  EXPECT_EQ(2, data.FirstBorderTileXIndexFromSrcCoord(179));
+  EXPECT_EQ(3, data.FirstBorderTileXIndexFromSrcCoord(180));
+  EXPECT_EQ(3, data.FirstBorderTileXIndexFromSrcCoord(199));
+
+  EXPECT_EQ(0, data.FirstBorderTileYIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.FirstBorderTileYIndexFromSrcCoord(79));
+  EXPECT_EQ(1, data.FirstBorderTileYIndexFromSrcCoord(80));
+  EXPECT_EQ(1, data.FirstBorderTileYIndexFromSrcCoord(99));
+  EXPECT_EQ(2, data.FirstBorderTileYIndexFromSrcCoord(100));
+  EXPECT_EQ(2, data.FirstBorderTileYIndexFromSrcCoord(119));
+  EXPECT_EQ(3, data.FirstBorderTileYIndexFromSrcCoord(120));
+  EXPECT_EQ(3, data.FirstBorderTileYIndexFromSrcCoord(139));
+  EXPECT_EQ(4, data.FirstBorderTileYIndexFromSrcCoord(140));
+  EXPECT_EQ(4, data.FirstBorderTileYIndexFromSrcCoord(144));
+
+  EXPECT_EQ(0, data.LastBorderTileXIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.LastBorderTileXIndexFromSrcCoord(39));
+  EXPECT_EQ(1, data.LastBorderTileXIndexFromSrcCoord(40));
+  EXPECT_EQ(1, data.LastBorderTileXIndexFromSrcCoord(79));
+  EXPECT_EQ(2, data.LastBorderTileXIndexFromSrcCoord(80));
+  EXPECT_EQ(2, data.LastBorderTileXIndexFromSrcCoord(119));
+  EXPECT_EQ(3, data.LastBorderTileXIndexFromSrcCoord(120));
+  EXPECT_EQ(3, data.LastBorderTileXIndexFromSrcCoord(199));
+
+  EXPECT_EQ(0, data.LastBorderTileYIndexFromSrcCoord(0));
+  EXPECT_EQ(0, data.LastBorderTileYIndexFromSrcCoord(19));
+  EXPECT_EQ(1, data.LastBorderTileYIndexFromSrcCoord(20));
+  EXPECT_EQ(1, data.LastBorderTileYIndexFromSrcCoord(39));
+  EXPECT_EQ(2, data.LastBorderTileYIndexFromSrcCoord(40));
+  EXPECT_EQ(2, data.LastBorderTileYIndexFromSrcCoord(59));
+  EXPECT_EQ(3, data.LastBorderTileYIndexFromSrcCoord(60));
+  EXPECT_EQ(3, data.LastBorderTileYIndexFromSrcCoord(79));
+  EXPECT_EQ(4, data.LastBorderTileYIndexFromSrcCoord(80));
+  EXPECT_EQ(4, data.LastBorderTileYIndexFromSrcCoord(144));
+}
+
+void TestIterate(const TilingData& data,
+                 gfx::Rect rect,
+                 int expect_left,
+                 int expect_top,
+                 int expect_right,
+                 int expect_bottom,
+                 bool include_borders) {
+  EXPECT_GE(expect_left, 0);
+  EXPECT_GE(expect_top, 0);
+  EXPECT_LT(expect_right, data.num_tiles_x());
+  EXPECT_LT(expect_bottom, data.num_tiles_y());
+
+  std::vector<std::pair<int, int> > original_expected;
+  for (int x = 0; x < data.num_tiles_x(); ++x) {
+    for (int y = 0; y < data.num_tiles_y(); ++y) {
+      gfx::Rect bounds;
+      if (include_borders)
+        bounds = data.TileBoundsWithBorder(x, y);
+      else
+        bounds = data.TileBounds(x, y);
+      if (x >= expect_left && x <= expect_right &&
+          y >= expect_top && y <= expect_bottom) {
+        EXPECT_TRUE(bounds.Intersects(rect));
+        original_expected.push_back(std::make_pair(x, y));
+      } else {
+        EXPECT_FALSE(bounds.Intersects(rect));
+      }
+    }
+  }
+
+  // Verify with vanilla iterator.
+  {
+    std::vector<std::pair<int, int> > expected = original_expected;
+    for (TilingData::Iterator iter(&data, rect, include_borders); iter;
+         ++iter) {
+      bool found = false;
+      for (size_t i = 0; i < expected.size(); ++i) {
+        if (expected[i] == iter.index()) {
+          expected[i] = expected.back();
+          expected.pop_back();
+          found = true;
+          break;
+        }
+      }
+      EXPECT_TRUE(found);
+    }
+    EXPECT_EQ(0u, expected.size());
+  }
+
+  // Make sure this also works with a difference iterator and an empty ignore.
+  // The difference iterator never includes borders, so ignore it otherwise.
+  if (!include_borders) {
+    std::vector<std::pair<int, int> > expected = original_expected;
+    for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect()); iter;
+         ++iter) {
+      bool found = false;
+      for (size_t i = 0; i < expected.size(); ++i) {
+        if (expected[i] == iter.index()) {
+          expected[i] = expected.back();
+          expected.pop_back();
+          found = true;
+          break;
+        }
+      }
+      EXPECT_TRUE(found);
+    }
+    EXPECT_EQ(0u, expected.size());
+  }
+}
+
+void TestIterateBorders(const TilingData& data,
+                        gfx::Rect rect,
+                        int expect_left,
+                        int expect_top,
+                        int expect_right,
+                        int expect_bottom) {
+  bool include_borders = true;
+  TestIterate(data,
+              rect,
+              expect_left,
+              expect_top,
+              expect_right,
+              expect_bottom,
+              include_borders);
+}
+
+void TestIterateNoBorders(const TilingData& data,
+                          gfx::Rect rect,
+                          int expect_left,
+                          int expect_top,
+                          int expect_right,
+                          int expect_bottom) {
+  bool include_borders = false;
+  TestIterate(data,
+              rect,
+              expect_left,
+              expect_top,
+              expect_right,
+              expect_bottom,
+              include_borders);
+}
+
+void TestIterateAll(const TilingData& data,
+                    gfx::Rect rect,
+                    int expect_left,
+                    int expect_top,
+                    int expect_right,
+                    int expect_bottom) {
+  TestIterateBorders(
+      data, rect, expect_left, expect_top, expect_right, expect_bottom);
+  TestIterateNoBorders(
+      data, rect, expect_left, expect_top, expect_right, expect_bottom);
+}
+
+TEST(TilingDataTest, IteratorNoBorderTexels) {
+  TilingData data(gfx::Size(10, 10), gfx::Size(40, 25), false);
+  // X border index by src coord: [0-10), [10-20), [20, 30), [30, 40)
+  // Y border index by src coord: [0-10), [10-20), [20, 25)
+  TestIterateAll(data, gfx::Rect(40, 25), 0, 0, 3, 2);
+  TestIterateAll(data, gfx::Rect(15, 15, 8, 8), 1, 1, 2, 2);
+
+  // Oversized.
+  TestIterateAll(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 3, 2);
+  TestIterateAll(data, gfx::Rect(-100, 20, 1000, 1), 0, 2, 3, 2);
+  TestIterateAll(data, gfx::Rect(29, -100, 31, 1000), 2, 0, 3, 2);
+  // Nonintersecting.
+  TestIterateAll(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, BordersIteratorOneBorderTexel) {
+  TilingData data(gfx::Size(10, 20), gfx::Size(25, 45), true);
+  // X border index by src coord: [0-10), [8-18), [16-25)
+  // Y border index by src coord: [0-20), [18-38), [36-45)
+  TestIterateBorders(data, gfx::Rect(25, 45), 0, 0, 2, 2);
+  TestIterateBorders(data, gfx::Rect(18, 19, 3, 17), 2, 0, 2, 1);
+  TestIterateBorders(data, gfx::Rect(10, 20, 6, 16), 1, 1, 1, 1);
+  TestIterateBorders(data, gfx::Rect(9, 19, 8, 18), 0, 0, 2, 2);
+  // Oversized.
+  TestIterateBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 2);
+  TestIterateBorders(data, gfx::Rect(-100, 20, 1000, 1), 0, 1, 2, 1);
+  TestIterateBorders(data, gfx::Rect(18, -100, 6, 1000), 2, 0, 2, 2);
+  // Nonintersecting.
+  TestIterateBorders(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, NoBordersIteratorOneBorderTexel) {
+  TilingData data(gfx::Size(10, 20), gfx::Size(25, 45), true);
+  // X index by src coord: [0-9), [9-17), [17-25)
+  // Y index by src coord: [0-19), [19-37), [37-45)
+  TestIterateNoBorders(data, gfx::Rect(25, 45), 0, 0, 2, 2);
+  TestIterateNoBorders(data, gfx::Rect(17, 19, 3, 18), 2, 1, 2, 1);
+  TestIterateNoBorders(data, gfx::Rect(17, 19, 3, 19), 2, 1, 2, 2);
+  TestIterateNoBorders(data, gfx::Rect(8, 18, 9, 19), 0, 0, 1, 1);
+  TestIterateNoBorders(data, gfx::Rect(9, 19, 9, 19), 1, 1, 2, 2);
+  // Oversized.
+  TestIterateNoBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 2);
+  TestIterateNoBorders(data, gfx::Rect(-100, 20, 1000, 1), 0, 1, 2, 1);
+  TestIterateNoBorders(data, gfx::Rect(18, -100, 6, 1000), 2, 0, 2, 2);
+  // Nonintersecting.
+  TestIterateNoBorders(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, BordersIteratorManyBorderTexels) {
+  TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+  // X border index by src coord: [0-50), [10-60), [20-65)
+  // Y border index by src coord: [0-60), [20-80), [40-100), [60-110)
+  TestIterateBorders(data, gfx::Rect(65, 110), 0, 0, 2, 3);
+  TestIterateBorders(data, gfx::Rect(50, 60, 15, 65), 1, 1, 2, 3);
+  TestIterateBorders(data, gfx::Rect(60, 30, 2, 10), 2, 0, 2, 1);
+  // Oversized.
+  TestIterateBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 3);
+  TestIterateBorders(data, gfx::Rect(-100, 10, 1000, 10), 0, 0, 2, 0);
+  TestIterateBorders(data, gfx::Rect(10, -100, 10, 1000), 0, 0, 1, 3);
+  // Nonintersecting.
+  TestIterateBorders(data, gfx::Rect(65, 110, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, NoBordersIteratorManyBorderTexels) {
+  TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+  // X index by src coord: [0-30), [30-40), [40, 65)
+  // Y index by src coord: [0-40), [40-60), [60, 80), [80-110)
+  TestIterateNoBorders(data, gfx::Rect(65, 110), 0, 0, 2, 3);
+  TestIterateNoBorders(data, gfx::Rect(30, 40, 15, 65), 1, 1, 2, 3);
+  TestIterateNoBorders(data, gfx::Rect(60, 20, 2, 21), 2, 0, 2, 1);
+  // Oversized.
+  TestIterateNoBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 3);
+  TestIterateNoBorders(data, gfx::Rect(-100, 10, 1000, 10), 0, 0, 2, 0);
+  TestIterateNoBorders(data, gfx::Rect(10, -100, 10, 1000), 0, 0, 0, 3);
+  // Nonintersecting.
+  TestIterateNoBorders(data, gfx::Rect(65, 110, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, IteratorOneTile) {
+  TilingData no_border(gfx::Size(1000, 1000), gfx::Size(30, 40), false);
+  TestIterateAll(no_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+  TestIterateAll(no_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+  TestIterateAll(no_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+
+  TilingData one_border(gfx::Size(1000, 1000), gfx::Size(30, 40), true);
+  TestIterateAll(one_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+  TestIterateAll(one_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+  TestIterateAll(one_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+
+  TilingData big_border(gfx::Size(1000, 1000), gfx::Size(30, 40), 50);
+  TestIterateAll(big_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+  TestIterateAll(big_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+  TestIterateAll(big_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, IteratorNoTiles) {
+  TilingData data(gfx::Size(100, 100), gfx::Size(), false);
+  TestIterateAll(data, gfx::Rect(100, 100), 0, 0, -1, -1);
+}
+
+void TestDiff(const TilingData& data,
+              gfx::Rect consider,
+              gfx::Rect ignore,
+              size_t num_tiles) {
+  std::vector<std::pair<int, int> > expected;
+  for (int y = 0; y < data.num_tiles_y(); ++y) {
+    for (int x = 0; x < data.num_tiles_x(); ++x) {
+      gfx::Rect bounds = data.TileBounds(x, y);
+      if (bounds.Intersects(consider) && !bounds.Intersects(ignore))
+        expected.push_back(std::make_pair(x, y));
+    }
+  }
+
+  // Sanity check the test.
+  EXPECT_EQ(num_tiles, expected.size());
+
+  for (TilingData::DifferenceIterator iter(&data, consider, ignore); iter;
+       ++iter) {
+    bool found = false;
+    for (size_t i = 0; i < expected.size(); ++i) {
+      if (expected[i] == iter.index()) {
+        expected[i] = expected.back();
+        expected.pop_back();
+        found = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(found);
+  }
+  EXPECT_EQ(0u, expected.size());
+}
+
+TEST(TilingDataTest, DifferenceIteratorIgnoreGeometry) {
+  // This test is checking that the iterator can handle different geometries of
+  // ignore rects relative to the consider rect.  The consider rect indices
+  // themselves are mostly tested by the non-difference iterator tests, so the
+  // full rect is mostly used here for simplicity.
+
+  // X border index by src coord: [0-10), [10-20), [20, 30), [30, 40)
+  // Y border index by src coord: [0-10), [10-20), [20, 25)
+  TilingData data(gfx::Size(10, 10), gfx::Size(40, 25), false);
+
+  // Fully ignored
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(40, 25), 0);
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(-100, -100, 200, 200), 0);
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(9, 9, 30, 15), 0);
+  TestDiff(data, gfx::Rect(15, 15, 8, 8), gfx::Rect(15, 15, 8, 8), 0);
+
+  // Fully un-ignored
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(-30, -20, 8, 8), 12);
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(), 12);
+
+  // Top left, remove 2x2 tiles
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 19), 8);
+  // Bottom right, remove 2x2 tiles
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 15, 20, 6), 8);
+  // Bottom left, remove 2x2 tiles
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 15, 20, 6), 8);
+  // Top right, remove 2x2 tiles
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 0, 20, 19), 8);
+  // Center, remove only one tile
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(10, 10, 5, 5), 11);
+
+  // Left column, flush left, removing two columns
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(11, 25), 6);
+  // Middle column, removing two columns
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(11, 0, 11, 25), 6);
+  // Right column, flush right, removing one column
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(30, 0, 2, 25), 9);
+
+  // Top row, flush top, removing one row
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 5, 40, 5), 8);
+  // Middle row, removing one row
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 13, 40, 5), 8);
+  // Bottom row, flush bottom, removing two rows
+  TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 13, 40, 12), 4);
+
+  // Non-intersecting, but still touching two of the same tiles.
+  TestDiff(data, gfx::Rect(8, 0, 32, 25), gfx::Rect(0, 12, 5, 12), 10);
+
+  // Intersecting, but neither contains the other. 2x3 with one overlap.
+  TestDiff(data, gfx::Rect(5, 2, 20, 10), gfx::Rect(25, 15, 5, 10), 5);
+}
+
+TEST(TilingDataTest, DifferenceIteratorManyBorderTexels) {
+  // X border index by src coord: [0-50), [10-60), [20-65)
+  // Y border index by src coord: [0-60), [20-80), [40-100), [60-110)
+  // X tile bounds by src coord: [0-30), [30-40), [40-65)
+  // Y tile bounds by src coord: [0-40), [40-60), [60-80), [80-110)
+  TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+
+  // Knock out two rows, but not the left column.
+  TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 2), 8);
+
+  // Knock out one row.
+  TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(29, 59, 20, 1), 9);
+
+  // Overlap all tiles with ignore rect.
+  TestDiff(data, gfx::Rect(65, 110), gfx::Rect(29, 39, 12, 42), 0);
+
+  gfx::Rect tile = data.TileBounds(1, 1);
+
+  // Ignore one tile.
+  TestDiff(data, gfx::Rect(20, 30, 45, 80), tile, 11);
+
+  // Include one tile.
+  TestDiff(data, tile, gfx::Rect(), 1);
+}
+
+TEST(TilingDataTest, DifferenceIteratorOneTile) {
+  TilingData no_border(gfx::Size(1000, 1000), gfx::Size(30, 40), false);
+  TestDiff(no_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+  TestDiff(no_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+
+  TilingData one_border(gfx::Size(1000, 1000), gfx::Size(30, 40), true);
+  TestDiff(one_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+  TestDiff(one_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+
+  TilingData big_border(gfx::Size(1000, 1000), gfx::Size(30, 40), 50);
+  TestDiff(big_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+  TestDiff(big_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+}
+
+TEST(TilingDataTest, DifferenceIteratorNoTiles) {
+  TilingData data(gfx::Size(100, 100), gfx::Size(), false);
+  TestDiff(data, gfx::Rect(100, 100), gfx::Rect(5, 5), 0);
+}
+
+void TestSpiralIterate(int source_line_number,
+                       const TilingData& tiling_data,
+                       const gfx::Rect& consider,
+                       const gfx::Rect& ignore,
+                       const gfx::Rect& center,
+                       const std::vector<std::pair<int, int> >& expected) {
+  std::vector<std::pair<int, int> > actual;
+  for (TilingData::SpiralDifferenceIterator it(
+           &tiling_data, consider, ignore, center);
+       it;
+       ++it) {
+    actual.push_back(it.index());
+  }
+
+  EXPECT_EQ(expected.size(), actual.size()) << "error from line "
+                                            << source_line_number;
+  for (size_t i = 0; i < std::min(expected.size(), actual.size()); ++i) {
+    EXPECT_EQ(expected[i].first, actual[i].first)
+        << "i: " << i << " error from line: " << source_line_number;
+    EXPECT_EQ(expected[i].second, actual[i].second)
+        << "i: " << i << " error from line: " << source_line_number;
+  }
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorNoIgnoreFullConsider) {
+  TilingData tiling_data(gfx::Size(10, 10), gfx::Size(30, 30), false);
+  gfx::Rect consider(30, 30);
+  gfx::Rect ignore;
+  std::vector<std::pair<int, int> > expected;
+
+  // Center is in the center of the tiling.
+  gfx::Rect center(15, 15, 1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 4 3 2
+  //  1| 5 * 1
+  //  2| 6 7 8
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(2, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center is off to the right side of the tiling (and far away).
+  center = gfx::Rect(100, 15, 1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 7 4 1
+  //  1| 8 5 2 *
+  //  2| 9 6 3
+  expected.clear();
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center is the bottom right corner of the tiling.
+  center = gfx::Rect(25, 25, 1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 6 5 4
+  //  1| 7 2 1
+  //  2| 8 3 *
+  expected.clear();
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center is off the top left side of the tiling.
+  center = gfx::Rect(-60, -50, 1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  // * x 0 1 2
+  //  y.------
+  //  0| 1 2 6
+  //  1| 3 4 5
+  //  2| 7 8 9
+  expected.clear();
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(2, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Two tile center.
+  center = gfx::Rect(15, 15, 1, 10);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 5 4 3
+  //  1| 6 * 2
+  //  2| 7 * 1
+  expected.clear();
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorSmallConsider) {
+  TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+  gfx::Rect ignore;
+  std::vector<std::pair<int, int> > expected;
+  gfx::Rect center(15, 15, 1, 1);
+
+  // Consider is one cell.
+  gfx::Rect consider(1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| 1
+  //  1|   *
+  //  2|
+  //  3|
+  //  4|
+  expected.push_back(std::make_pair(0, 0));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Consider is bottom right corner.
+  consider = gfx::Rect(25, 25, 10, 10);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0|
+  //  1|   *
+  //  2|     1 2
+  //  3|     3 4
+  //  4|
+  expected.clear();
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(3, 2));
+  expected.push_back(std::make_pair(2, 3));
+  expected.push_back(std::make_pair(3, 3));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Consider is one column.
+  consider = gfx::Rect(11, 0, 1, 100);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0|   2
+  //  1|   *
+  //  2|   3
+  //  3|   4
+  //  4|   5
+  expected.clear();
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(1, 3));
+  expected.push_back(std::make_pair(1, 4));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorHasIgnore) {
+  TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+  gfx::Rect consider(50, 50);
+  std::vector<std::pair<int, int> > expected;
+  gfx::Rect center(15, 15, 1, 1);
+
+  // Full ignore.
+  gfx::Rect ignore(50, 50);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| . . . . .
+  //  1| . * . . .
+  //  2| . . . . .
+  //  3| . . . . .
+  //  4| . . . . .
+  expected.clear();
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // 3 column ignore.
+  ignore = gfx::Rect(15, 0, 20, 100);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| 1 . . . 8
+  //  1| 2 * . . 7
+  //  2| 3 . . . 6
+  //  3| 4 . . . 5
+  //  4| 9 . . . 10
+  expected.clear();
+
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(0, 3));
+  expected.push_back(std::make_pair(4, 3));
+  expected.push_back(std::make_pair(4, 2));
+  expected.push_back(std::make_pair(4, 1));
+  expected.push_back(std::make_pair(4, 0));
+  expected.push_back(std::make_pair(0, 4));
+  expected.push_back(std::make_pair(4, 4));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Ignore covers the top half.
+  ignore = gfx::Rect(50, 25);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| . . . . .
+  //  1| . * . . .
+  //  2| . . . . .
+  //  3| 1 2 3 4 5
+  //  4| 6 7 8 9 10
+  expected.clear();
+
+  expected.push_back(std::make_pair(0, 3));
+  expected.push_back(std::make_pair(1, 3));
+  expected.push_back(std::make_pair(2, 3));
+  expected.push_back(std::make_pair(3, 3));
+  expected.push_back(std::make_pair(4, 3));
+  expected.push_back(std::make_pair(0, 4));
+  expected.push_back(std::make_pair(1, 4));
+  expected.push_back(std::make_pair(2, 4));
+  expected.push_back(std::make_pair(3, 4));
+  expected.push_back(std::make_pair(4, 4));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorRectangleCenter) {
+  TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+  gfx::Rect consider(50, 50);
+  std::vector<std::pair<int, int> > expected;
+  gfx::Rect ignore;
+
+  // Two cell center
+  gfx::Rect center(25, 25, 1, 10);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| J I H G F
+  //  1| K 5 4 3 E
+  //  2| L 6 * 2 D
+  //  3| M 7 * 1 C
+  //  4| N 8 9 A B
+  expected.clear();
+
+  expected.push_back(std::make_pair(3, 3));
+  expected.push_back(std::make_pair(3, 2));
+  expected.push_back(std::make_pair(3, 1));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(1, 3));
+  expected.push_back(std::make_pair(1, 4));
+  expected.push_back(std::make_pair(2, 4));
+  expected.push_back(std::make_pair(3, 4));
+  expected.push_back(std::make_pair(4, 4));
+  expected.push_back(std::make_pair(4, 3));
+  expected.push_back(std::make_pair(4, 2));
+  expected.push_back(std::make_pair(4, 1));
+  expected.push_back(std::make_pair(4, 0));
+  expected.push_back(std::make_pair(3, 0));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(0, 3));
+  expected.push_back(std::make_pair(0, 4));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Three by two center.
+  center = gfx::Rect(15, 25, 20, 10);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2 3 4
+  //  y.----------
+  //  0| J I H G F
+  //  1| 7 6 5 4 3
+  //  2| 8 * * * 2
+  //  3| 9 * * * 1
+  //  4| A B C D E
+  expected.clear();
+
+  expected.push_back(std::make_pair(4, 3));
+  expected.push_back(std::make_pair(4, 2));
+  expected.push_back(std::make_pair(4, 1));
+  expected.push_back(std::make_pair(3, 1));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(0, 3));
+  expected.push_back(std::make_pair(0, 4));
+  expected.push_back(std::make_pair(1, 4));
+  expected.push_back(std::make_pair(2, 4));
+  expected.push_back(std::make_pair(3, 4));
+  expected.push_back(std::make_pair(4, 4));
+  expected.push_back(std::make_pair(4, 0));
+  expected.push_back(std::make_pair(3, 0));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Column center off the left side.
+  center = gfx::Rect(-50, 0, 30, 50);
+
+  // Layout of the tiling data, and expected return order:
+  //    x 0 1 2 3 4
+  //   y.----------
+  // * 0| 5 A F K P
+  // * 1| 4 9 E J O
+  // * 2| 3 8 D I N
+  // * 3| 2 7 C H M
+  // * 4| 1 6 B G L
+  expected.clear();
+
+  expected.push_back(std::make_pair(0, 4));
+  expected.push_back(std::make_pair(0, 3));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(1, 4));
+  expected.push_back(std::make_pair(1, 3));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(2, 4));
+  expected.push_back(std::make_pair(2, 3));
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(3, 4));
+  expected.push_back(std::make_pair(3, 3));
+  expected.push_back(std::make_pair(3, 2));
+  expected.push_back(std::make_pair(3, 1));
+  expected.push_back(std::make_pair(3, 0));
+  expected.push_back(std::make_pair(4, 4));
+  expected.push_back(std::make_pair(4, 3));
+  expected.push_back(std::make_pair(4, 2));
+  expected.push_back(std::make_pair(4, 1));
+  expected.push_back(std::make_pair(4, 0));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorEdgeCases) {
+  TilingData tiling_data(gfx::Size(10, 10), gfx::Size(30, 30), false);
+  std::vector<std::pair<int, int> > expected;
+  gfx::Rect center;
+  gfx::Rect consider;
+  gfx::Rect ignore;
+
+  // Ignore contains, but is not equal to, consider and center.
+  ignore = gfx::Rect(15, 0, 20, 30);
+  consider = gfx::Rect(20, 10, 10, 20);
+  center = gfx::Rect(25, 0, 5, 5);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0|   . *
+  //  1|   . .
+  //  2|   . .
+  expected.clear();
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center intersects with consider.
+  ignore = gfx::Rect();
+  center = gfx::Rect(0, 15, 30, 15);
+  consider = gfx::Rect(15, 30);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 2 1
+  //  1| * * *
+  //  2| * * *
+  expected.clear();
+
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 0));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Consider and ignore are non-intersecting.
+  ignore = gfx::Rect(5, 30);
+  consider = gfx::Rect(25, 0, 5, 30);
+  center = gfx::Rect(15, 0, 1, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| . * 1
+  //  1| .   2
+  //  2| .   3
+  expected.clear();
+
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center intersects with ignore.
+  consider = gfx::Rect(30, 30);
+  center = gfx::Rect(15, 0, 1, 30);
+  ignore = gfx::Rect(0, 15, 30, 1);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 3 * 2
+  //  1| . * .
+  //  2| 4 * 1
+  expected.clear();
+
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center and ignore are the same.
+  consider = gfx::Rect(30, 30);
+  center = gfx::Rect(15, 0, 1, 30);
+  ignore = center;
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  //  0| 4 * 3
+  //  1| 5 * 2
+  //  2| 6 * 1
+  expected.clear();
+
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Empty tiling data.
+  TilingData empty_data(gfx::Size(0, 0), gfx::Size(0, 0), false);
+
+  expected.clear();
+  TestSpiralIterate(__LINE__, empty_data, consider, ignore, center, expected);
+
+  // Empty consider.
+  ignore = gfx::Rect();
+  center = gfx::Rect(1, 1, 1, 1);
+  consider = gfx::Rect();
+
+  expected.clear();
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Empty center. Note: This arbitrarily puts the center to be off the top-left
+  // corner.
+  consider = gfx::Rect(30, 30);
+  ignore = gfx::Rect();
+  center = gfx::Rect();
+
+  // Layout of the tiling data, and expected return order:
+  // * x 0 1 2
+  //  y.------
+  //  0| 1 2 6
+  //  1| 3 4 5
+  //  2| 7 8 9
+  expected.clear();
+
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(2, 2));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Every rect is empty.
+  ignore = gfx::Rect();
+  center = gfx::Rect();
+  consider = gfx::Rect();
+
+  expected.clear();
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+  // Center is just to the left of cover, and off of the tiling's left side.
+  consider = gfx::Rect(30, 30);
+  ignore = gfx::Rect();
+  center = gfx::Rect(-20, 0, 19, 30);
+
+  // Layout of the tiling data, and expected return order:
+  //   x 0 1 2
+  //  y.------
+  // *0| 3 6 9
+  // *1| 2 5 8
+  // *2| 1 4 7
+  expected.clear();
+
+  expected.push_back(std::make_pair(0, 2));
+  expected.push_back(std::make_pair(0, 1));
+  expected.push_back(std::make_pair(0, 0));
+  expected.push_back(std::make_pair(1, 2));
+  expected.push_back(std::make_pair(1, 1));
+  expected.push_back(std::make_pair(1, 0));
+  expected.push_back(std::make_pair(2, 2));
+  expected.push_back(std::make_pair(2, 1));
+  expected.push_back(std::make_pair(2, 0));
+
+  TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+}  // namespace
+
+}  // namespace cc
diff --git a/cc/base/unique_notifier.cc b/cc/base/unique_notifier.cc
new file mode 100644
index 0000000..0656c6d
--- /dev/null
+++ b/cc/base/unique_notifier.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+UniqueNotifier::UniqueNotifier(base::SequencedTaskRunner* task_runner,
+                               const base::Closure& closure)
+    : task_runner_(task_runner),
+      closure_(closure),
+      notification_pending_(false),
+      weak_ptr_factory_(this) {
+}
+
+UniqueNotifier::~UniqueNotifier() {
+}
+
+void UniqueNotifier::Schedule() {
+  if (notification_pending_)
+    return;
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&UniqueNotifier::Notify, weak_ptr_factory_.GetWeakPtr()));
+  notification_pending_ = true;
+}
+
+void UniqueNotifier::Notify() {
+  // Note that the order here is important in case closure schedules another
+  // run.
+  notification_pending_ = false;
+  closure_.Run();
+}
+
+}  // namespace cc
diff --git a/cc/base/unique_notifier.h b/cc/base/unique_notifier.h
new file mode 100644
index 0000000..89bfecc
--- /dev/null
+++ b/cc/base/unique_notifier.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_UNIQUE_NOTIFIER_H_
+#define CC_BASE_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace cc {
+
+class CC_EXPORT UniqueNotifier {
+ public:
+  // Configure this notifier to issue the |closure| notification when scheduled.
+  UniqueNotifier(base::SequencedTaskRunner* task_runner,
+                 const base::Closure& closure);
+
+  // Destroying the notifier will ensure that no further notifications will
+  // happen from this class.
+  ~UniqueNotifier();
+
+  // Schedule a notification to be run. If another notification is already
+  // pending, then only one notification will take place.
+  void Schedule();
+
+ private:
+  void Notify();
+
+  // TODO(dcheng): How come this doesn't need to hold a ref to the task runner?
+  base::SequencedTaskRunner* task_runner_;
+  base::Closure closure_;
+  bool notification_pending_;
+
+  base::WeakPtrFactory<UniqueNotifier> weak_ptr_factory_;
+};
+
+}  // namespace cc
+
+#endif  // CC_BASE_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/unique_notifier_unittest.cc b/cc/base/unique_notifier_unittest.cc
new file mode 100644
index 0000000..ec1b83c
--- /dev/null
+++ b/cc/base/unique_notifier_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/base/unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class UniqueNotifierTest : public testing::Test {
+ public:
+  UniqueNotifierTest() : notification_count_(0) {}
+
+  virtual void SetUp() OVERRIDE { ResetNotificationCount(); }
+
+  void Notify() { ++notification_count_; }
+
+  int NotificationCount() const { return notification_count_; }
+
+  void ResetNotificationCount() { notification_count_ = 0; }
+
+ protected:
+  int notification_count_;
+};
+
+TEST_F(UniqueNotifierTest, Schedule) {
+  {
+    UniqueNotifier notifier(
+        base::MessageLoopProxy::current().get(),
+        base::Bind(&UniqueNotifierTest::Notify, base::Unretained(this)));
+
+    EXPECT_EQ(0, NotificationCount());
+
+    // Basic schedule should result in a run.
+    notifier.Schedule();
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(1, NotificationCount());
+
+    // Multiple schedules should only result in one run.
+    for (int i = 0; i < 5; ++i)
+      notifier.Schedule();
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(2, NotificationCount());
+
+    notifier.Schedule();
+  }
+
+  // Notifier went out of scope, so we don't expect to get a notification.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, NotificationCount());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/base/util.h b/cc/base/util.h
new file mode 100644
index 0000000..1d716ae
--- /dev/null
+++ b/cc/base/util.h
@@ -0,0 +1,29 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_BASE_UTIL_H_
+#define CC_BASE_UTIL_H_
+
+#include <limits>
+
+#include "base/basictypes.h"
+
+namespace cc {
+
+template <typename T> T RoundUp(T n, T mul) {
+  COMPILE_ASSERT(std::numeric_limits<T>::is_integer, type_must_be_integral);
+  return (n > 0) ? ((n + mul - 1) / mul) * mul
+                 : (n / mul) * mul;
+}
+
+template <typename T> T RoundDown(T n, T mul) {
+  COMPILE_ASSERT(std::numeric_limits<T>::is_integer, type_must_be_integral);
+  return (n > 0) ? (n / mul) * mul
+                 : (n == 0) ? 0
+                 : ((n - mul + 1) / mul) * mul;
+}
+
+}  // namespace cc
+
+#endif  // CC_BASE_UTIL_H_
diff --git a/cc/base/util_unittest.cc b/cc/base/util_unittest.cc
new file mode 100644
index 0000000..6665a6a
--- /dev/null
+++ b/cc/base/util_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/base/util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(UtilTest, RoundUp) {
+  for (int multiplier = 1; multiplier <= 10; ++multiplier) {
+    // Try attempts in descending order, so that we can
+    // determine the correct value before it's needed.
+    int correct;
+    for (int attempt = 5 * multiplier; attempt >= -5 * multiplier; --attempt) {
+      if ((attempt % multiplier) == 0)
+        correct = attempt;
+      EXPECT_EQ(correct, RoundUp(attempt, multiplier))
+          << "attempt=" << attempt << " multiplier=" << multiplier;
+    }
+  }
+
+  for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
+    // Try attempts in descending order, so that we can
+    // determine the correct value before it's needed.
+    unsigned correct;
+    for (unsigned attempt = 5 * multiplier; attempt > 0; --attempt) {
+      if ((attempt % multiplier) == 0)
+        correct = attempt;
+      EXPECT_EQ(correct, RoundUp(attempt, multiplier))
+          << "attempt=" << attempt << " multiplier=" << multiplier;
+    }
+    EXPECT_EQ(0u, RoundUp(0u, multiplier))
+        << "attempt=0 multiplier=" << multiplier;
+  }
+}
+
+TEST(UtilTest, RoundDown) {
+  for (int multiplier = 1; multiplier <= 10; ++multiplier) {
+    // Try attempts in ascending order, so that we can
+    // determine the correct value before it's needed.
+    int correct;
+    for (int attempt = -5 * multiplier; attempt <= 5 * multiplier; ++attempt) {
+      if ((attempt % multiplier) == 0)
+        correct = attempt;
+      EXPECT_EQ(correct, RoundDown(attempt, multiplier))
+          << "attempt=" << attempt << " multiplier=" << multiplier;
+    }
+  }
+
+  for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
+    // Try attempts in ascending order, so that we can
+    // determine the correct value before it's needed.
+    unsigned correct;
+    for (unsigned attempt = 0; attempt <= 5 * multiplier; ++attempt) {
+      if ((attempt % multiplier) == 0)
+        correct = attempt;
+      EXPECT_EQ(correct, RoundDown(attempt, multiplier))
+          << "attempt=" << attempt << " multiplier=" << multiplier;
+    }
+  }
+}
+
+}  // namespace
+}  // namespace cc